diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..e54fe4d14 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.github +cache +dist +docker-compose* +.env* +Dockerfile diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..e467a87af --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +REVERSE_PROXY_PORT=8000 +REVERSE_PROXY_UI_PORT=8080 +CFG_VERSION=latest +CGW_VERSION=latest +TXS_VERSION=latest +UI_VERSION=latest +RPC_NODE_URL=http://host.docker.internal:8545 +ETHEREUM_NODE_URL=http://host.docker.internal:8545 \ No newline at end of file diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml new file mode 100644 index 000000000..2927076d4 --- /dev/null +++ b/.github/workflows/api.yml @@ -0,0 +1,237 @@ +name: API CI + +on: + push: + +jobs: + buildAndTest: + name: CI Pipeline + runs-on: ubuntu-latest + strategy: + matrix: + node-version: ['18'] # Add other versions if needed + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Install Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + + - name: Restore Node.js dependencies + uses: actions/cache@v3 + with: + path: ./node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Node.js dependencies + run: yarn install --frozen-lockfile + + - name: Cache Node.js dependencies + uses: actions/cache/save@v3 + if: always() + with: + path: ./node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + + - name: Start Hardhat RPC + run: | + yarn hardhat > /dev/null & + yarn hardhat-deploy + + - name: Start Safe Transaction Service + run: | + docker compose --env-file=.env.example -f docker-compose.safe.yml up -d --remove-orphans + docker compose exec txs-web python manage.py insert_safe_master_copy --address "0xd916a690676e925Ac9Faf2d01869c13Fd9757ef2" + sudo chmod -R a+rwx ./docker/data + + - name: Run Tests + env: + NODE_OPTIONS: --max-old-space-size=8192 + run: | + docker compose -f docker-compose.yml -f docker-compose.api.yml run \ + -e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \ + -e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \ + -e NODE_OPTIONS='--max-old-space-size=8192' \ + -T api \ + npx nx run api:test --verbose + + # - name: SonarCloud Scan + # uses: SonarSource/sonarcloud-github-action@master + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # with: + # projectBaseDir: apps/api + + bumpVersion: + name: 'Bump Version on develop' + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: buildAndTest + outputs: + newTag: ${{ steps.version-bump.outputs.newTag }} + + steps: + - name: 'Checkout source code' + uses: 'actions/checkout@v2' + with: + ref: ${{ github.ref }} + + - name: 'Automated Version Bump' + id: version-bump + uses: 'phips28/gh-action-bump-version@master' + with: + tag-prefix: 'v' + tag-suffix: '-api' + commit-message: 'CI: bumps version to {{version}}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PACKAGEJSON_DIR: 'apps/api' + + buildAndPushImage: + name: Build and Push docker image + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: bumpVersion + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-3 + + - name: Login to ECR + uses: docker/login-action@v1 + with: + registry: 275440070213.dkr.ecr.eu-west-3.amazonaws.com + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: | + 275440070213.dkr.ecr.eu-west-3.amazonaws.com/api + tags: | + type=ref,event=branch + type=sha + type=semver,pattern={{version}},value=${{needs.bumpVersion.outputs.newTag}} + type=semver,pattern={{major}}.{{minor}},value=${{needs.bumpVersion.outputs.newTag}} + type=semver,pattern={{raw}},value=${{needs.bumpVersion.outputs.newTag}} + + - name: Set correct version + run: npm version ${{needs.bumpVersion.outputs.newTag}} --allow-same-version=true --git-tag-version=false + working-directory: ./apps/api + + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: apps/api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + buildAndPushHotfixImage: + name: Build and Push hotfix docker image + runs-on: ubuntu-latest + if: startsWith(github.ref,'refs/heads/hotfix/') + needs: buildAndTest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-3 + + - name: Login to ECR + uses: docker/login-action@v1 + with: + registry: 275440070213.dkr.ecr.eu-west-3.amazonaws.com + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: | + 275440070213.dkr.ecr.eu-west-3.amazonaws.com/api + tags: | + type=ref,event=branch + type=sha + + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: apps/api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + autodeploy: + name: Auto deploy develop to dev.api.thx.network + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: [buildAndPushImage, bumpVersion] + steps: + - name: Install Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + + - name: Install deploy-scripts + run: npm install -g thxprotocol/deploy-scripts + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-3 + + - name: Deploy-script + run: thx-deploy ApiDev sha-$(echo ${{github.sha}} | cut -c1-7) + + discord: + name: Update Discord + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: [autodeploy, bumpVersion] + steps: + - name: Send message + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_AWS_WEBHOOK }} + uses: Ilshidur/action-discord@master + with: + args: "${{ needs.autodeploy.result == 'success' && '✅' || '⛔' }} Released APIDev `${{ needs.bumpVersion.outputs.newTag }}`" diff --git a/.github/workflows/auth.yml b/.github/workflows/auth.yml new file mode 100644 index 000000000..9f8e4d689 --- /dev/null +++ b/.github/workflows/auth.yml @@ -0,0 +1,188 @@ +name: Auth CI + +on: + push: + +jobs: + buildAndTest: + name: CI Pipeline + runs-on: ubuntu-latest + strategy: + matrix: + node-version: ['18'] # Add other versions if needed + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Run test + run: docker compose -f docker-compose.yml -f docker-compose.auth.yml run -T auth sh -c "npx nx lint auth && npx nx test auth" + + # - name: SonarCloud Scan + # uses: SonarSource/sonarcloud-github-action@master + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # with: + # projectBaseDir: apps/auth + + bumpVersion: + name: 'Bump Version on develop' + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: buildAndTest + outputs: + newTag: ${{ steps.version-bump.outputs.newTag }} + + steps: + - name: 'Checkout source code' + uses: 'actions/checkout@v2' + with: + ref: ${{ github.ref }} + + - name: 'Automated Version Bump' + id: version-bump + uses: 'phips28/gh-action-bump-version@master' + with: + tag-prefix: 'v' + tag-suffix: '-auth' + commit-message: 'CI: bumps version to {{version}}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PACKAGEJSON_DIR: 'apps/auth' + + buildAndPushImage: + name: Build and Push docker image + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: bumpVersion + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: 16.x + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-3 + + - name: Login to ECR + uses: docker/login-action@v1 + with: + registry: 275440070213.dkr.ecr.eu-west-3.amazonaws.com + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: | + 275440070213.dkr.ecr.eu-west-3.amazonaws.com/auth + tags: | + type=ref,event=branch + type=sha + type=semver,pattern={{version}},value=${{needs.bumpVersion.outputs.newTag}} + type=semver,pattern={{major}}.{{minor}},value=${{needs.bumpVersion.outputs.newTag}} + type=semver,pattern={{raw}},value=${{needs.bumpVersion.outputs.newTag}} + + - name: Set correct version + run: npm version ${{needs.bumpVersion.outputs.newTag}} --allow-same-version=true --git-tag-version=false + working-directory: ./apps/auth + + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: apps/auth/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + buildAndPushHotfixImage: + name: Build and Push Hotfix docker image + runs-on: ubuntu-latest + if: startsWith(github.ref,'refs/heads/hotfix/') + needs: buildAndTest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: 16.x + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-3 + + - name: Login to ECR + uses: docker/login-action@v1 + with: + registry: 275440070213.dkr.ecr.eu-west-3.amazonaws.com + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: | + 275440070213.dkr.ecr.eu-west-3.amazonaws.com/auth + tags: | + type=ref,event=branch + type=sha + + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + push: true + file: apps/auth/Dockerfile + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + autodeploy: + name: Auto deploy develop to dev.auth.thx.network + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: [buildAndPushImage, bumpVersion] + steps: + - name: Install Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: 16.x + + - name: Install deploy-scripts + run: npm install -g thxprotocol/deploy-scripts + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-3 + + - name: Deploy-script + run: thx-deploy AuthDev sha-$(echo ${{github.sha}} | cut -c1-7) + + discord: + name: Update Discord + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + needs: [autodeploy, bumpVersion] + steps: + - name: Send message + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_AWS_WEBHOOK }} + uses: Ilshidur/action-discord@master + with: + args: "${{ needs.autodeploy.result == 'success' && '✅' || '⛔' }} Released AuthDev `${{ needs.bumpVersion.outputs.newTag }}`" diff --git a/.gitignore b/.gitignore index 66be901ef..cdbe75633 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,7 @@ Thumbs.db !.env.example certs/* -.nx/cache \ No newline at end of file +.nx/cache + +# Docker +docker/data/**/* \ No newline at end of file diff --git a/apps/api/.env.example b/apps/api/.env.example new file mode 100644 index 000000000..1b4d383ac --- /dev/null +++ b/apps/api/.env.example @@ -0,0 +1,41 @@ +MONGODB_URI="mongodb://root:root@localhost:27017/api?authSource=admin&ssl=false" +MONGODB_URI_TEST_OVERRIDE="mongodb://root:root@localhost:27017/api_test?authSource=admin&ssl=false" +MONGODB_NAME="api" +MONGODB_USER="root" +MONGODB_PASSWORD="root" +AUTH_URL="https://local.auth.thx.network" +API_URL="https://localhost:3001" +DASHBOARD_URL="https://localhost:8082" +WALLET_URL="https://localhost:8083" +WIDGET_URL="https://localhost:8080" +HARDHAT_RPC="https://localhost:8547" +HARDHAT_RPC_TEST_OVERRIDE="http://127.0.0.1:8545" +POLYGON_RPC="" +ETHEREUM_RPC="" +PRIVATE_KEY="0x873c254263b17925b686f971d7724267710895f1585bb0533db8e693a2af32ff" +INITIAL_ACCESS_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +PORT="3000" +AUTH_CLIENT_ID="ISsh6Xw6wDISihJS2xs2_" +AUTH_CLIENT_SECRET="BnQrAHSb4UDnpR-9klfFAQbZvq_RjVZgLTKwRD3qfOjawX21jrnbBxvSOU84EwqAy1J-fNKvxD2qZen5Gm8jXg" +INFURA_PROJECT_ID="" +INFURA_IPFS_PROJECT_ID="" +INFURA_IPFS_PROJECT_SECRET="" +RATE_LIMIT_REWARD_GIVE="100" +RATE_LIMIT_REWARD_GIVE_WINDOW="900" +MAX_FEE_PER_GAS="400" +POLYGON_NAME="maticdev" +POLYGON_MUMBAI_NAME="mumbaidev" +SAFE_TXS_SERVICE="http://localhost:8000/txs" +HARDHAT_NAME="hardhat" +AWS_ACCESS_KEY_ID="" +AWS_SECRET_ACCESS_KEY="" +AWS_S3_PUBLIC_BUCKET_NAME="local-thx-storage-bucket" +AWS_S3_PUBLIC_BUCKET_REGION="eu-west-3" +AWS_S3_PRIVATE_BUCKET_NAME="local-thx-private-storage-bucket" +AWS_S3_PRIVATE_BUCKET_REGION="eu-west-3" +POLYGON_RELAYER="" +POLYGON_RELAYER_API_KEY="" +POLYGON_RELAYER_API_SECRET="" +LOCAL_CERT="../../certs/localhost.crt" +LOCAL_CERT_KEY="../../certs/localhost.key" +CWD="/usr/src/app/apps/api/src/" \ No newline at end of file diff --git a/apps/api/.eslintrc.json b/apps/api/.eslintrc.json new file mode 100644 index 000000000..5626944bd --- /dev/null +++ b/apps/api/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile new file mode 100644 index 000000000..365597cd4 --- /dev/null +++ b/apps/api/Dockerfile @@ -0,0 +1,60 @@ +##################################################################################################### +## Develop stage +##################################################################################################### +FROM node:18-slim AS develop + +WORKDIR /usr/src/app + +ENV NODE_OPTIONS="--max_old_space_size=8192" + +RUN apt-get update \ + && apt-get install -y g++ make python3-pip build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +COPY package.json yarn.lock ./ +RUN yarn +COPY . . + +CMD [ "npx", "nx", "serve", "api" ] + +##################################################################################################### +## Build stage +##################################################################################################### +FROM node:18-slim AS build + +ENV NODE_ENV=production + +WORKDIR /usr/src/app +COPY --from=develop ./usr/src/app/ ./ +RUN npx nx build api --prod +COPY ./newrelic.js ./yarn.lock ./dist/apps/api/ +COPY ./libs/contracts/exports ./dist/apps/api/libs/contracts/exports + +##################################################################################################### +## Production stage +##################################################################################################### +FROM node:18-slim AS production + +ENV NODE_ENV=production + +WORKDIR /usr/src/app +COPY --from=build ./usr/src/app/dist/apps/api/package.json ./usr/src/app/dist/apps/api/yarn.lock ./ + +# Install dependencies and packages +RUN apt-get update \ + && apt-get install -y g++ make python3-pip build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +# Install your application dependencies (assuming it uses Node.js) +RUN yarn + +# Clean up unnecessary packages and files +RUN apt-get purge -y --auto-remove build-essential && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=build ./usr/src/app/dist/apps/api ./ + +CMD [ "main.js" ] \ No newline at end of file diff --git a/apps/api/jest.config.ts b/apps/api/jest.config.ts new file mode 100644 index 000000000..f0a4a6364 --- /dev/null +++ b/apps/api/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'api', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/api', +}; diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 000000000..ee78b15a1 --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,20 @@ +{ + "name": "@thxnetwork/api", + "contributors": [ + "Peter Polman <peter@thx.network>", + "Valeria Grazzini <vgrazzini@gmail.com>", + "GarfDev <garfdev.13@gmail.com>", + "Bram Rongen <mail@bramrongen.nl>", + "Justina Mary <justinamary27@gmail.com>", + "Evert Kors <evert@hatching.io>", + "jochemvn <jochemvn@gmail.com>" + ], + "license": "AGPL-3.0", + "version": "1.52.132", + "scripts": { + "migrate": "yarn run migrate:db && yarn run migrate:contracts", + "migrate:contracts": "node upgradeContractsToLatest.js", + "migrate:db": "node migrate-mongo.js up", + "migrate:db:down": "node migrate-mongo.js down" + } +} diff --git a/apps/api/project.json b/apps/api/project.json new file mode 100644 index 000000000..7961a9c19 --- /dev/null +++ b/apps/api/project.json @@ -0,0 +1,127 @@ +{ + "name": "api", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/api/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "@nx/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "target": "node", + "compiler": "tsc", + "outputPath": "dist/apps/api", + "main": "apps/api/src/main.ts", + "tsConfig": "apps/api/tsconfig.app.json", + "assets": ["apps/api/src/assets", "apps/api/src/app/migrations"], + "webpackConfig": "apps/api/webpack.config.js", + "generatePackageJson": true, + "additionalEntryPoints": [ + { + "entryPath": "apps/api/scripts/upgradeContractsToLatest.ts", + "entryName": "upgradeContractsToLatest" + }, + { + "entryPath": "apps/api/scripts/migrate-mongo.ts", + "entryName": "migrate-mongo" + }, + { + "entryPath": "apps/api/scripts/script.ts", + "entryName": "script" + } + ] + }, + "configurations": { + "development": {}, + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false + } + } + }, + "serve": { + "executor": "@nx/js:node", + "options": { + "buildTarget": "api:build", + "host": "localhost", + "port": 3000, + "inspect": false, + "watch": true + }, + "configurations": { + "production": { + "buildTarget": "api:build:production" + } + } + }, + "hardhat": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "npx hardhat node --hostname 0.0.0.0", + "cwd": "apps/api/src/app/hardhat" + } + }, + "hardhat-deploy": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "npx hardhat run scripts/deploy.ts --network localhost", + "cwd": "apps/api/src/app/hardhat" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/api/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/api/jest.config.ts", + "testTimeout": 60000, + "passWithNoTests": false, + "bail": false, + "runInBand": true, + "logHeapUsage": true + } + }, + "script": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "node script.js", + "cwd": "dist/apps/api" + } + }, + "migrate-contracts": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "node upgradeContractsToLatest.js", + "cwd": "dist/apps/api" + } + }, + "migrate-db": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "node migrate-mongo.js up", + "cwd": "dist/apps/api" + } + }, + "migrate-db-create": { + "executor": "nx:run-commands", + "options": { + "command": "migrate-mongo create -f src/app/config/migrate-mongo-create-only.json", + "cwd": "apps/api" + } + } + } +} diff --git a/apps/api/scripts/migrate-mongo.ts b/apps/api/scripts/migrate-mongo.ts new file mode 100644 index 000000000..eca6d7799 --- /dev/null +++ b/apps/api/scripts/migrate-mongo.ts @@ -0,0 +1,4 @@ +import { migrateMongoScript } from '@thxnetwork/common/index'; +import migrateMongoConfig from '../src/app/config/migrate-mongo'; + +migrateMongoScript(migrateMongoConfig); diff --git a/apps/api/scripts/script.ts b/apps/api/scripts/script.ts new file mode 100644 index 000000000..db419c30c --- /dev/null +++ b/apps/api/scripts/script.ts @@ -0,0 +1,23 @@ +import db from '@thxnetwork/api/util/database'; +import main from './src/veLiquidity'; +// import main from './src/veTransfer'; +// import main from './src/veRewards'; +// import main from './src/time'; +// import main from './src/galachain'; +// import main from './src/sdk'; +// import main from './src/vethx'; +// import main from './src/safe'; +// import main from './src/ipfs'; +// import main from './src/invoices'; +// import main from './src/demo'; +// import main from './src/preview'; +// import main from './src/metamask'; + +db.connect(process.env.MONGODB_URI_PROD); + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/apps/api/scripts/src/demo.ts b/apps/api/scripts/src/demo.ts new file mode 100644 index 000000000..42a652de3 --- /dev/null +++ b/apps/api/scripts/src/demo.ts @@ -0,0 +1,267 @@ +import path from 'path'; +import db from '@thxnetwork/api/util/database'; +import { ChainId, QuestVariant, QuestSocialRequirement, AccessTokenKind } from '@thxnetwork/common/enums'; +import { Widget } from '@thxnetwork/api/models/Widget'; +import { + readCSV, + getYoutubeID, + getTwitterUsername, + createPool, + getSuggestion, + getTwitterTWeet, + getTwitterUser, +} from './utils/index'; +import { RewardCoin } from '@thxnetwork/api/models/RewardCoin'; +import { Collaborator } from '@thxnetwork/api/models/Collaborator'; +import { Pool, QuestDaily, QuestInvite, QuestSocial, QuestCustom } from '@thxnetwork/api/models'; + +const csvFilePath = path.join(__dirname, '../../../', 'quests.csv'); +// const sub = '60a38b36bf804b033c5e3faa'; // Local +const sub = '6074cbdd1459355fae4b6a14'; // Peter +const sub2 = '655d0c5dde9eca4f50999423'; // Prasanth +const sub3 = '627d06fbb0d159d419292240'; // Mieszko +const collaborators = [ + { + sub, + email: 'peter@thx.network', + }, + { + sub: sub2, + email: 'mieszko@thx.network', + }, + { + sub: sub3, + email: 'prasanth@thx.network', + }, +]; + +// const chainId = ChainId.Hardhat; +const chainId = ChainId.Polygon; +// const erc20Id = '64d3a4149f7e6d78c9366982'; // Local +const erc20Id = '6464c665633c1cf385d8cc2b'; // THX Network (POS) on Prod + +export default async function main() { + const start = Date.now(); + const skipped = []; + const results = []; + console.log('Start', new Date()); + + let tokens = 0; + try { + const data: any = await readCSV(csvFilePath); + + for (const sql of data) { + const videoUrl = sql['Youtube Video URL']; + const tweetUrl = sql['Twitter Tweet URL']; + const serverId = sql['Discord Server ID']; + const missingMaterials = sql['Sales Material'] === 'Missing'; + const gameName = sql['Game']; + const gameDomain = sql['Game Domain']; + + if (!missingMaterials || !gameName || !gameDomain) continue; + + let pool = await Pool.findOne({ 'settings.title': gameName }); + console.log('==============='); + console.log('Import: ', gameName, gameDomain); + if (pool) { + await Widget.updateOne({ poolId: pool._id }, { domain: new URL(gameDomain).origin }); + } else { + pool = await createPool(sub, gameName, gameDomain); + } + + await pool.updateOne({ chainId: ChainId.Polygon }); + + const poolId = pool._id; + await pool.updateOne({ chainId }); + + // Remove all existing data + await Promise.all([ + QuestDaily.deleteMany({ poolId }), + QuestInvite.deleteMany({ poolId }), + QuestSocial.deleteMany({ poolId }), + QuestCustom.deleteMany({ poolId }), + RewardCoin.deleteMany({ poolId, erc20Id }), + Collaborator.deleteMany({ poolId }), + ]); + + // Create social quest youtube like + if (videoUrl && videoUrl !== 'N/A') { + const videoId = getYoutubeID(videoUrl); + const socialQuestYoutubeLike = { + poolId, + index: 0, + uuid: db.createUUID(), + title: `Watch & Like`, + description: '', + amount: 75, + kind: AccessTokenKind.Google, + interaction: QuestSocialRequirement.YouTubeLike, + content: videoId, + contentMetadata: JSON.stringify({ videoId }), + isPublished: true, + variant: QuestVariant.YouTube, + }; + await QuestSocial.create(socialQuestYoutubeLike); + } + + // Create social quest twitter retweet + if (tweetUrl && tweetUrl !== 'N/A') { + const username = getTwitterUsername(tweetUrl); + const twitterUser = await getTwitterUser(username); + const socialQuestFollow = { + poolId, + index: 1, + uuid: db.createUUID(), + title: `Follow ${sql['Game']}`, + description: '', + amount: 75, + kind: AccessTokenKind.Twitter, + interaction: QuestSocialRequirement.TwitterFollow, + content: twitterUser.id, + variant: QuestVariant.Twitter, + contentMetadata: JSON.stringify({ + id: twitterUser.id, + name: twitterUser.name, + username: twitterUser.username, + profileImgUrl: twitterUser.profile_image_url, + }), + isPublished: true, + }; + await QuestSocial.create(socialQuestFollow); + + const tweetId = tweetUrl.match(/\/(\d+)(?:\?|$)/)[1]; + const [tweet] = await getTwitterTWeet(tweetId); + const socialQuestRetweet = { + poolId, + uuid: db.createUUID(), + variant: QuestVariant.Twitter, + title: `Boost Tweet!`, + description: '', + amount: 50, + kind: AccessTokenKind.Twitter, + interaction: QuestSocialRequirement.TwitterLikeRetweet, + content: tweetId, + contentMetadata: JSON.stringify({ + url: tweetUrl, + username: twitterUser.username, + text: tweet.text, + }), + isPublished: true, + }; + await QuestSocial.create(socialQuestRetweet); + } + + // Create social quest twitter retweet + if (serverId && serverId !== 'N/A') { + const inviteURL = sql['Discord Invite URL']; + const socialQuestJoin = { + poolId, + variant: QuestVariant.Discord, + uuid: db.createUUID(), + title: `Join ${gameName} Discord`, + description: 'Become a part of our fam!', + amount: 75, + kind: AccessTokenKind.Discord, + interaction: QuestSocialRequirement.DiscordGuildJoined, + content: serverId, + contentMetadata: JSON.stringify({ serverId, inviteURL: inviteURL || undefined }), + isPublished: true, + }; + await QuestSocial.create(socialQuestJoin); + } + + // Create default erc20 rewards + await RewardCoin.create({ + poolId, + uuid: db.createUUID(), + title: `Small bag of $THX`, + description: 'A token of appreciation offered to you by THX Network. Could also be your own token!', + image: 'https://thx-storage-bucket.s3.eu-west-3.amazonaws.com/widget-referral-xmzfznsqschvqxzvgn47qo-xtencq4fmgjg7qgwewmybj-(1)-8EHr7ckbrEZLqUyxqJK1LG.png', + pointPrice: 1000, + limit: 1000, + amount: 10, + erc20Id, + isPublished: true, + }); + + // Create colloborators + for (const c of collaborators) { + await Collaborator.create({ + poolId, + sub: c.sub, + state: 1, + uuid: db.createUUID(), + email: c.email, + }); + } + + // Iterate over available quests and create + for (let i = 1; i < 3; i++) { + const questType = sql[`Q${i} - Type`]; + const points = sql[`Q${i} - Points`]; + const title = sql[`Q${i} - Title`]; + if (!questType || !points || !title) { + console.log(`Incomplete Q${i}!`); + continue; + } + + let titleSuggestion, descriptionSuggestion; + if (['Daily', 'Custom'].includes(questType)) { + titleSuggestion = await getSuggestion(sql[`Q${i} - Title`], 40); + // titleSuggestion = sql[`Q${i} - Title`]; + tokens += titleSuggestion.tokensUsed; + descriptionSuggestion = await getSuggestion(sql[`Q${i} - Description`], 100); + // descriptionSuggestion = sql[`Q${i} - Description`]; + tokens += descriptionSuggestion.tokensUsed; + } + + switch (questType) { + case 'Daily': { + const dailyQuest = { + poolId, + variant: QuestVariant.Daily, + title: titleSuggestion.content, + description: descriptionSuggestion.content, + amounts: [5, 10, 20, 40, 80, 160, 360], + isPublished: true, + }; + await QuestDaily.create(dailyQuest); + console.log(sql[`Q${i} - Type`], titleSuggestion.content, 'quest created!'); + break; + } + case 'Custom': { + const customQuest = { + poolId, + variant: QuestVariant.Custom, + title: titleSuggestion.content, + description: descriptionSuggestion.content, + amount: Number(sql[`Q${i} - Points`]), + limit: 0, + isPublished: true, + }; + await QuestCustom.create(customQuest); + console.log(sql[`Q${i} - Type`], titleSuggestion.content, 'quest created!'); + break; + } + default: { + console.log(sql[`Q${i} - Type`], 'quest skipped...'); + } + } + } + results.push([sql['Game'], `https://dashboard.thx.network/preview/${poolId}`]); + } + } catch (err) { + console.error(err); + } + console.log('==============='); + console.log('COPY BELOW INTO SHEET'); + console.log('==============='); + results.forEach((item) => { + console.log(`${item[0]}\t${item[1]}`); + }); + console.log('==============='); + console.log('Skipped', skipped); + console.log('End', new Date()); + console.log('Duration', Date.now() - start, 'seconds'); + console.log('Tokens Spent', tokens); +} diff --git a/apps/api/scripts/src/galachain.ts b/apps/api/scripts/src/galachain.ts new file mode 100644 index 000000000..a1b34faf8 --- /dev/null +++ b/apps/api/scripts/src/galachain.ts @@ -0,0 +1,83 @@ +import { ethers } from 'ethers'; +import { AllowanceType } from '@gala-chain/api'; +import { GalachainContract, getContract } from '../../src/app/util/galachain'; +import GalachainService from '../../src/app/services/GalachainService'; +import BigNumber from 'bignumber.js'; + +const PRIVATE_KEY_ADMIN = '62172f65ecab45f423f7088128eee8946c5b3c03911cb0b061b1dd9032337271'; +const PRIVATE_KEY_DISTRIBUTOR = '096b2543a26e164e5f8887c737fe31d04734abe657416eacf0b5a52e6c5fa684'; +const PRIVATE_KEY_USER = '97093724e1748ebfa6aa2d2ec4ec68df8678423ab9a12eb2d27ddc74e35e5db9'; + +export default async function main() { + const nftClassKey = { + collection: 'Weapons', + category: 'Blades', + type: 'none', + additionalKey: 'none', + }; + // Define coin class key + const tokenInfo = { + decimals: 0, + tokenClass: nftClassKey, + name: 'Sting', + symbol: 'WBSting', + description: 'This collection holds weapons of any sort!', + image: 'https://pbs.twimg.com/profile_images/1640708099177877505/4U-ya--t_400x400.jpg', + isNonFungible: true, + maxSupply: new BigNumber(100), + }; + const profileContract = getContract(GalachainContract.PublicKeyContract); + const tokenContract = getContract(GalachainContract.GalaChainToken); + + // Generate random wallet + const walletAdmin = new ethers.Wallet(PRIVATE_KEY_ADMIN); + console.log({ walletAdmin }); + + const walletDistributor = new ethers.Wallet(PRIVATE_KEY_DISTRIBUTOR); + console.log({ walletDistributor }); + + const walletUser = new ethers.Wallet(PRIVATE_KEY_USER); + console.log({ walletUser }); + + // Register distributor + const distributor = await GalachainService.registerUser( + profileContract, + walletDistributor.publicKey, + PRIVATE_KEY_ADMIN, + ); + console.log(distributor); + + // Register user + const user = await GalachainService.registerUser(profileContract, walletUser.publicKey, PRIVATE_KEY_ADMIN); + console.log(user); + + // Admin creates an NFT + const nft = await GalachainService.create(tokenContract, tokenInfo, nftClassKey, PRIVATE_KEY_ADMIN); + console.log(nft); + + // Approve minting of maxsupply for admin + const result = await GalachainService.approve( + tokenContract, + nftClassKey, + walletDistributor.address, + 100, + AllowanceType.Mint, + PRIVATE_KEY_ADMIN, + ); + console.log(result); + + // Distributor mints 5 tokens + await GalachainService.mint(tokenContract, nftClassKey, walletDistributor.address, 5, PRIVATE_KEY_DISTRIBUTOR); + + // Get balance of tokens for distributor + const balances = (await GalachainService.balanceOf(tokenContract, nftClassKey, PRIVATE_KEY_DISTRIBUTOR)) as { + quantity: number; + }[]; + console.log(balances); + + // Balance of the user + const [balanceUser] = (await GalachainService.balanceOf(tokenContract, nftClassKey, PRIVATE_KEY_USER)) as { + quantity: number; + }[]; + console.log(balanceUser); +} diff --git a/apps/api/scripts/src/invoices.ts b/apps/api/scripts/src/invoices.ts new file mode 100644 index 000000000..ae4d752b7 --- /dev/null +++ b/apps/api/scripts/src/invoices.ts @@ -0,0 +1,16 @@ +import InvoiceService from '@thxnetwork/api/services/InvoiceService'; +import { startOfMonth, endOfMonth, addHours, subMonths } from 'date-fns'; + +export default async function main() { + const currentDate = subMonths(new Date(), 0); + const invoicePeriodstartDate = startOfMonth(currentDate); + const invoicePeriodEndDate = endOfMonth(currentDate); + + // Account for UTC + 2 timezone offset + const offset = 2; + + await InvoiceService.upsertInvoices( + addHours(invoicePeriodstartDate, offset), + addHours(invoicePeriodEndDate, offset), + ); +} diff --git a/apps/api/scripts/src/ipfs.ts b/apps/api/scripts/src/ipfs.ts new file mode 100644 index 000000000..d7b3d08f6 --- /dev/null +++ b/apps/api/scripts/src/ipfs.ts @@ -0,0 +1,29 @@ +import { ERC721 } from '@thxnetwork/api/models/ERC721'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; +import pinataSDK from '@pinata/sdk'; +import axios from 'axios'; + +const pinata = new pinataSDK({ pinataJWTKey: process.env.PINATA_API_JWT }); + +class PinataIPFS { + static async addURL(url: string) { + const response = await axios.get(url, { responseType: 'stream' }); + const urlParts = url.split('/'); + const name = urlParts[urlParts.length - 1]; + const { IpfsHash } = await pinata.pinFileToIPFS(response.data, { + pinataMetadata: { name }, + pinataOptions: { cidVersion: 0 }, + }); + return IpfsHash; + } +} + +export default async function main() { + const nft = await ERC721.findById('64c8f15e01506efa24c1c72c'); + const metadataList = await ERC721Metadata.find({ erc721Id: nft._id }); + for (const metadata of metadataList) { + const imgCid = await PinataIPFS.addURL(metadata.imageUrl); + const metadataCid = await PinataIPFS.addURL('https://api.thx.network/v1/metadata/' + String(metadata._id)); + console.log(metadata.name, String(metadata._id), imgCid, metadataCid); + } +} diff --git a/apps/api/scripts/src/metamask.ts b/apps/api/scripts/src/metamask.ts new file mode 100644 index 000000000..7700240d4 --- /dev/null +++ b/apps/api/scripts/src/metamask.ts @@ -0,0 +1,136 @@ +import path from 'path'; +import fs from 'fs'; +import { MongoClient, Db } from 'mongodb'; +import { Wallet } from '@thxnetwork/api/models/Wallet'; +import { QuestSocialEntry } from '@thxnetwork/api/models/QuestSocialEntry'; +import { + ERC1155Token, + ERC20Token, + ERC721Token, + Participant, + Pool, + QuestCustomEntry, + QuestDailyEntry, + QuestGitcoinEntry, + QuestWeb3Entry, + RewardCoinPayment, + RewardCouponPayment, + RewardCustomPayment, + RewardDiscordRolePayment, + RewardNFTPayment, +} from '@thxnetwork/api/models'; + +export default async function main() { + const filePath = path.join(__dirname, '../../../metamask-accounts.csv'); + const client = new MongoClient(process.env.MONGODB_URI_AUTH_PROD); + + await client.connect(); + + const db: Db = client.db('auth-prod'); + const accounts = await db.collection('accounts').find({ variant: 4 }).toArray(); + const subs = accounts.map(({ _id }) => String(_id)); + const wallets = await Wallet.find({ + sub: { $in: subs }, + address: { $exists: true }, + $and: [{ version: { $exists: false } }, { safeVersion: { $exists: false } }], + }); + const participants = await Participant.find({ sub: { $in: subs } }); + const poolIds = participants.map((p) => p.poolId); + const pools = await Pool.find({ _id: { $in: poolIds } }); + + const csvData = await Promise.all( + accounts.map(async (account) => { + const sub = String(account._id); + const w = wallets.find((p) => p.sub === sub); + const p = participants.find((p) => p.sub === sub); + const pool = p && pools.find((pool) => String(pool._id) === p.poolId); + + const [ + dailyCount, + socialCount, + customCount, + web3Count, + gitcoinCount, + erc20Count, + erc721Count, + erc1155Count, + coinCount, + nftCount, + discordCount, + customrewardCount, + couponCount, + ] = await Promise.all( + [ + QuestDailyEntry, + QuestSocialEntry, + QuestCustomEntry, + QuestWeb3Entry, + QuestGitcoinEntry, + // Coins + ERC20Token, + // NFT + ERC721Token, + ERC1155Token, + // RewardPayments + RewardCoinPayment, + RewardNFTPayment, + RewardDiscordRolePayment, + RewardCustomPayment, + RewardCouponPayment, + ].map((Model) => Model.countDocuments({ sub })), + ); + + return [ + sub, + pool && pool.settings && pool.settings.title, + p && p.balance, + p && p.score, + p && p.questEntryCount, + p && p.updatedAt, + w && w.address, + dailyCount, + socialCount, + customCount, + web3Count, + gitcoinCount, + erc20Count, + erc721Count, + erc1155Count, + coinCount, + nftCount, + discordCount, + customrewardCount, + couponCount, + ].join(','); + }), + ); + + csvData.push( + [ + 'sub', + 'campaign', + 'balance', + 'score', + 'questEntryCount', + 'updatedAt', + 'address', + 'dailyCount', + 'socialCount', + 'customCount', + 'web3Count', + 'gitcoinCoun', + 'erc20Count', + 'erc721Count', + 'erc1155Count', + 'coinCount', + 'nftCount', + 'discordCount', + 'customrewardCount', + 'couponCount', + ].join(','), + ); + + fs.writeFileSync(filePath, csvData.reverse().join('\n'), 'utf8'); +} +// This query finds all wallets that have an address and do not have a version nor safeVersion field. Indicating they are metamask wallets +// { $and: [{ version: { $exists: false} }, { safeVersion: { $exists: false } }], sub: { $exists: true }, poolId: { $exists: true } } diff --git a/apps/api/scripts/src/preview.ts b/apps/api/scripts/src/preview.ts new file mode 100644 index 000000000..5b15277d8 --- /dev/null +++ b/apps/api/scripts/src/preview.ts @@ -0,0 +1,177 @@ +import puppeteer from 'puppeteer'; +import fs from 'fs'; +import db from '@thxnetwork/api/util/database'; +import { Brand, Widget } from '@thxnetwork/api/models'; +import { createCanvas, loadImage, registerFont } from 'canvas'; +import { assetsPath } from '@thxnetwork/api/util/path'; +import path from 'path'; +import CanvasService from '@thxnetwork/api/services/CanvasService'; + +// Provide before running +const poolIds = ['660f101c4a0130f6f8315762', '660f10e4e298a7a04bbb35ae']; + +// Load on boot as registration on runtime results in font not being loaded in time +const fontPath = path.resolve(assetsPath, 'fa-solid-900.ttf'); +const family = 'Font Awesome 5 Pro Solid'; +const defaultBackgroundImgPath = path.resolve(assetsPath, 'bg.png'); +const defaultLogoImgPath = path.resolve(assetsPath, 'logo.png'); + +// ENV +// const widgetBaseUrl = 'https://dev-app.thx.network'; +const widgetBaseUrl = 'https://app.thx.network'; + +registerFont(fontPath, { family, style: 'normal', weight: '900' }); + +// db.connect(process.env.MONGODB_URI); +// db.connect(process.env.MONGODB_URI_DEV); +db.connect(process.env.MONGODB_URI_PROD); + +async function createCampaignWidgetPreviewImage({ poolId, logoImgUrl, backgroundImgUrl }: TBrand) { + const widget = await Widget.findOne({ poolId }); + if (!widget) return; + const theme = JSON.parse(widget.theme); + + const rightOffset = 20; + const bottomOffset = 90; + const widgetHeight = 700; + const widgetWidth = 400; + + // Get screenshot image + const widgetUrl = `${widgetBaseUrl}/c/${poolId}/quests`; + const fileName = `${poolId}.jpg`; + + // Can not use asset path here on runtime + const outputPath = path.resolve(__dirname, fileName); + await captureScreenshot(widgetUrl, outputPath, widgetWidth, widgetHeight); + + // Read screenshot from disk + const file = fs.readFileSync(outputPath); + if (!file) throw new Error('Screenshot failed'); + + // Load the base64 image data into an Image object + const bg = await loadImage(backgroundImgUrl || defaultBackgroundImgPath); + const logo = await loadImage(logoImgUrl || defaultLogoImgPath); + const screenshot = await loadImage(file); + + // Create a canvas with the desired dimensions + const canvasHeight = widgetHeight + bottomOffset + rightOffset; // 810 + const canvasWidth = Math.floor((canvasHeight / 9) * 16); // 1440 + const canvas = createCanvas(canvasWidth, canvasHeight); + const ctx = canvas.getContext('2d'); + + // Draw the loaded image onto the canvas + CanvasService.drawImageBg(canvas, ctx, bg); + + // Draw the logo + const logoRatio = logo.width / logo.height; + const logoWidth = 200; + const logoHeight = logoWidth / logoRatio; + + ctx.drawImage(logo, canvasWidth / 2 - logoWidth / 2, canvasHeight / 2 - logoHeight / 2, logoWidth, logoHeight); + + const launcherRadius = 30; + const launcherCenterOffset = 50; + + // Draw the launcher circle + ctx.beginPath(); + ctx.arc(canvasWidth - launcherCenterOffset, canvasHeight - launcherCenterOffset, launcherRadius, 0, 2 * Math.PI); + ctx.fillStyle = theme.elements.launcherBg.color; + ctx.fill(); + + const notificationRadius = 10; + const notificationX = canvasWidth - launcherCenterOffset - launcherRadius / 2 - notificationRadius / 2; + const notificationY = canvasHeight - launcherCenterOffset - launcherRadius / 2 - notificationRadius / 2; + + // Draw the launcher icon + const fontSizeIcon = 20; + ctx.font = `900 ${fontSizeIcon}px "${family}"`; + ctx.fillStyle = theme.elements.launcherIcon.color; //; + ctx.fillText( + `\uf06b`, + canvasWidth - launcherCenterOffset - fontSizeIcon / 2, + canvasHeight - launcherCenterOffset + fontSizeIcon / 3, + ); + + // Draw the notification circle + ctx.beginPath(); + ctx.arc(notificationX, notificationY, notificationRadius, 0, 2 * Math.PI); + ctx.fillStyle = 'red'; + ctx.fill(); + + // Draw the notificition counter + const fontSizeNotification = 16; + ctx.font = `bold normal ${fontSizeNotification}px "Arial"`; + ctx.fillStyle = 'white'; + ctx.fillText('3', notificationX - notificationRadius / 2, notificationY + notificationRadius / 2); + + // Draw the widget screenshot + const borderRadius = 10; + const widgetX = canvasWidth - widgetWidth - rightOffset; + const widgetY = canvasHeight - widgetHeight - bottomOffset; + + // Round the borders by clipping + drawImageRounded(ctx, widgetX, widgetY, widgetWidth, widgetHeight, borderRadius); + ctx.clip(); + ctx.drawImage(screenshot, widgetX, widgetY, widgetWidth, widgetHeight); + + // Convert the canvas content to a buffer + // const dataUrl = canvas.toDataURL('image/png'); + const buffer = canvas.toBuffer('image/png'); + + return buffer; +} + +export async function captureScreenshot(url, outputFileName, width, height) { + const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); + + const browser = await puppeteer.launch({ headless: 'new' }); + const page = await browser.newPage(); + + await page.setViewport({ width, height }); + await page.goto(url); + + // Collapse CSS animation needs to finish + await delay(3000); + + await page.screenshot({ path: outputFileName }); + + await browser.close(); +} + +function drawImageRounded(ctx, x, y, width, height, radius) { + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); +} + +export default async function main() { + const start = Date.now(); + + console.log('Start', new Date()); + const brands = await Brand.find({ poolId: { $in: poolIds } }); + for (const index in brands) { + try { + const brand = brands[index]; + const previewFile = await createCampaignWidgetPreviewImage(brand); + if (!previewFile) continue; + // Write the image buffer data to the file + const output = path.join('/Users/peterpolman/Desktop/previews', `${brand.poolId}.png`); + fs.writeFileSync(output, previewFile); + + console.log(`${Number(index) + 1}/${brands.length} ${brand.poolId}`); + } catch (error) { + console.error(brands[index].poolId, error); + } + } + + console.log('End', new Date()); + console.log('Duration', Date.now() - start, 'seconds'); +} diff --git a/apps/api/scripts/src/safe.ts b/apps/api/scripts/src/safe.ts new file mode 100644 index 000000000..883ead62c --- /dev/null +++ b/apps/api/scripts/src/safe.ts @@ -0,0 +1,31 @@ +import { safeVersion } from '@thxnetwork/api/services/ContractService'; +import { toChecksumAddress } from 'web3-utils'; +import { Wallet } from '@thxnetwork/api/models/Wallet'; +import { ChainId } from '@thxnetwork/common/enums'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { SafeFactory } from '@safe-global/protocol-kit'; + +export default async function main() { + const SAFE = toChecksumAddress(''); // Provide values + const RELAYER = toChecksumAddress(''); // Provide values + const ACCOUNT = toChecksumAddress(''); // Provide values + const wallet = await Wallet.findOne({ + address: SAFE, + chainId: ChainId.Polygon, + }); + if (SAFE !== wallet.address) throw new Error('Provided address does not equal Safe address.'); + + const { ethAdapter } = getProvider(ChainId.Polygon); + const safeFactory = await SafeFactory.create({ + safeVersion, + ethAdapter, + }); + const safeAccountConfig = { + owners: [RELAYER, ACCOUNT], + threshold: 2, + }; + const safeAddress = await safeFactory.predictSafeAddress(safeAccountConfig); + console.log(safeAddress); + + await safeFactory.deploySafe({ safeAccountConfig, options: { gasLimit: '3000000' } }); +} diff --git a/apps/api/scripts/src/sdk.ts b/apps/api/scripts/src/sdk.ts new file mode 100644 index 000000000..e3b278c01 --- /dev/null +++ b/apps/api/scripts/src/sdk.ts @@ -0,0 +1,12 @@ +import { THXAPIClient } from '@thxnetwork/sdk/clients'; +import { API_URL, AUTH_URL } from '@thxnetwork/api/config/secrets'; + +export default async function main() { + const thx = new THXAPIClient({ + authUrl: AUTH_URL, + apiUrl: API_URL, + clientId: 'BitG_fGJI5k70kQgEeyID', + clientSecret: 'pniCrGc49hb_l18_MrpahhJC8SexAV1nHE9RR9CkZA2qA_YbRmJd1hSHl5fcpJA1ngmRwuoys47JfLtYJDlSgA', + }); + await thx.events.create({ event: 'test', identity: '4de81b20-c71d-11ee-ac82-a970a9e4ebc4' }); +} diff --git a/apps/api/scripts/src/time.ts b/apps/api/scripts/src/time.ts new file mode 100644 index 000000000..0c2f65ad5 --- /dev/null +++ b/apps/api/scripts/src/time.ts @@ -0,0 +1,14 @@ +import { ethers } from 'ethers'; + +export async function increaseBlockTime(provider, seconds) { + await provider.send('evm_increaseTime', [seconds]); + await provider.send('evm_mine', []); +} + +export default async function main() { + const HARDHAT_RPC = 'http://127.0.0.1:8545/'; + const hardhatProvider = new ethers.providers.JsonRpcProvider(HARDHAT_RPC); + + // Travel past first week else this throws "Reward distribution has not started yet" + await increaseBlockTime(hardhatProvider, 60 * 60 * 24 * 7); +} diff --git a/apps/api/scripts/src/utils/index.ts b/apps/api/scripts/src/utils/index.ts new file mode 100644 index 000000000..4e3d9cffb --- /dev/null +++ b/apps/api/scripts/src/utils/index.ts @@ -0,0 +1,98 @@ +import fs from 'fs'; +import OpenAI from 'openai'; +import csvParser from 'csv-parser'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { DEFAULT_COLORS, DEFAULT_ELEMENTS } from '@thxnetwork/common/constants'; +import { Widget } from '@thxnetwork/api/models/Widget'; +import { v4 } from 'uuid'; +import { twitterClient } from '@thxnetwork/api/util/twitter'; + +async function readCSV(csvFilePath: string) { + const data: any = []; + + return new Promise((resolve, reject) => { + fs.createReadStream(csvFilePath) + .pipe(csvParser()) + .on('data', (row) => { + data.push(row); + }) + .on('end', () => { + console.log('CSV data read successfully:'); + resolve(data); + }) + .on('error', (err) => { + reject(new Error('Error while reading CSV: ' + err.message)); + }); + }); +} + +function getYoutubeID(url) { + if (url && url.toLowerCase().includes('shorts')) return; + + const result = /^https?:\/\/(www\.)?youtu\.be/.test(url) + ? url.replace(/^https?:\/\/(www\.)?youtu\.be\/([\w-]{11}).*/, '$2') + : url.replace(/.*\?v=([\w-]{11}).*/, '$1'); + + return result; +} + +function getTwitterUsername(url: string) { + return url.split('/')[3]; +} + +async function createPool(sub: string, title: string, gameUrl: string) { + const pool = await PoolService.deploy(sub, title); + + await Widget.create({ + uuid: v4(), + poolId: pool._id, + align: 'right', + message: 'Hi there!👋 Click me to earn rewards with quests...', + domain: new URL(gameUrl).origin, + theme: JSON.stringify({ elements: DEFAULT_ELEMENTS, colors: DEFAULT_COLORS }), + }); + + return pool; +} + +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +async function getSuggestion(content: string, length: number) { + if (!content) return; + + const prompt = `You are a content writer for games. Please rephrase this text in a short, engaging and active form. Apply a maximum character length of ${length} characters:`; + const data = await openai.chat.completions.create({ + model: 'gpt-3.5-turbo', + messages: [{ role: 'user', content: prompt + content }], + }); + + return { + content: data.choices[0].message.content, + tokensUsed: Number(data.usage.total_tokens), + }; +} +async function getTwitterTWeet(tweetId: string) { + const { data } = await twitterClient({ + method: 'GET', + url: `/tweets`, + params: { + ids: tweetId, + expansions: 'author_id', + }, + }); + return data.data; +} +async function getTwitterUser(username: string) { + const { data } = await twitterClient({ + method: 'GET', + url: `/users/by/username/${username}`, + params: { + 'user.fields': 'profile_image_url', + }, + }); + return data.data; +} + +export { readCSV, getYoutubeID, getTwitterUsername, createPool, getSuggestion, getTwitterTWeet, getTwitterUser }; diff --git a/apps/api/scripts/src/veLiquidity.ts b/apps/api/scripts/src/veLiquidity.ts new file mode 100644 index 000000000..33020e1d5 --- /dev/null +++ b/apps/api/scripts/src/veLiquidity.ts @@ -0,0 +1,47 @@ +import { ethers } from 'ethers'; +import { parseUnits } from 'ethers/lib/utils'; +import { PRIVATE_KEY } from '@thxnetwork/api/config/secrets'; +import { getArtifact, contractNetworks } from '@thxnetwork/api/hardhat'; +import { ChainId } from '@thxnetwork/common/enums'; + +async function increaseBlockTime(provider, seconds) { + await provider.send('evm_increaseTime', [seconds]); + await provider.send('evm_mine', []); +} + +export default async function main() { + const HARDHAT_RPC = 'http://127.0.0.1:8545/'; + const hardhatProvider = new ethers.providers.JsonRpcProvider(HARDHAT_RPC); + // const TO = '0x029E2d4D2b6938c92c48dbf422a4e500425a08D8'; + const TO = '0xaf9d56684466fcFcEA0a2B7fC137AB864d642946'; + // const TO = '0x7b8fc09eb5D80eadA6AE74b112463eA006DC25B5'; + const AMOUNT_USDC = parseUnits('12300', 6).toString(); + const AMOUNT_THX = parseUnits('45600', 18).toString(); + const chainId = ChainId.Hardhat; + const signer = new ethers.Wallet(PRIVATE_KEY, hardhatProvider) as unknown as ethers.Signer; + + const usdc = new ethers.Contract(contractNetworks[chainId].USDC, getArtifact('USDC').abi, signer); + await usdc.transfer(TO, AMOUNT_USDC); + + const thx = new ethers.Contract(contractNetworks[chainId].THX, getArtifact('THX').abi, signer); + await thx.transfer(TO, AMOUNT_THX); + + // Increase time till past veTHX reward distribution start time + await increaseBlockTime(hardhatProvider, 60 * 60 * 24 * 7); + + // const usdcPerMonthInWei = ethers.utils.parseUnits('146700', 6); + // const secondsPerMonth = 30 * 24 * 60 * 60; + // const usdcPerSecond = usdcPerMonthInWei.div(secondsPerMonth); + // const splitter = new ethers.Contract( + // contractNetworks[chainId].THXPaymentSplitter, + // contractArtifacts['THXPaymentSplitter'].abi, + // signer, + // ); + // await splitter.setRate(TO, usdcPerSecond); + // const rate = await splitter.rates(TO); + // console.log(usdcPerSecond.toString(), rate.toString()); + + // await usdc.approve(splitter.address, usdcPerMonthInWei); + // await splitter.deposit(TO, usdcPerMonthInWei); + // console.log((await splitter.balanceOf(TO)).toString()); +} diff --git a/apps/api/scripts/src/veRewards.ts b/apps/api/scripts/src/veRewards.ts new file mode 100644 index 000000000..76a796ad2 --- /dev/null +++ b/apps/api/scripts/src/veRewards.ts @@ -0,0 +1,48 @@ +import { PRIVATE_KEY } from '@thxnetwork/api/config/secrets'; +import { contractNetworks, getArtifact } from '@thxnetwork/api/hardhat'; +import { ChainId } from '@thxnetwork/common/enums'; +import { ethers } from 'ethers'; +import { parseUnits } from 'ethers/lib/utils'; +import { increaseBlockTime } from './time'; + +export default async function main() { + const HARDHAT_RPC = 'http://127.0.0.1:8545/'; + const hardhatProvider = new ethers.providers.JsonRpcProvider(HARDHAT_RPC); + const chainId = ChainId.Hardhat; + const signer = new ethers.Wallet(PRIVATE_KEY, hardhatProvider) as unknown as ethers.Signer; + const bpt = new ethers.Contract(contractNetworks[chainId].BPT, getArtifact('BPT').abi, signer); + const bal = new ethers.Contract(contractNetworks[chainId].BAL, getArtifact('BAL').abi, signer); + const rewardFaucet = new ethers.Contract( + contractNetworks[chainId].RewardFaucet, + getArtifact('RewardFaucet').abi, + signer, + ); + const AMOUNTBAL = parseUnits('100').toString(); + const AMOUNTBPT = parseUnits('1000').toString(); + + // Travel past reward distribution start time + await increaseBlockTime(hardhatProvider, 60 * 60 * 24 * 7); + + // Deposit reward tokens into rdthx + await bal.approve(rewardFaucet.address, AMOUNTBAL); + await bpt.approve(rewardFaucet.address, AMOUNTBPT); + + // // Claim all pending BAL rewards and prepare for distriubtion + // // await ve.claimExternalRewards(); + // // Mock externalRewards by transfering directly into rd + // await bal.transfer(rd.address, AMOUNT); + + // Make sure to redistribute past rewards before depositing + // await rf.distributePastRewards(bpt.address); + // await rf.distributePastRewards(bal.address); + + // Spread the amount evenly over 4 weeks from the current block + // Can only run after reward distribution has started + const tx1 = await rewardFaucet.depositEqualWeeksPeriod(bal.address, AMOUNTBAL, '4'); + console.log(await tx1.wait()); + const tx2 = await rewardFaucet.depositEqualWeeksPeriod(bpt.address, AMOUNTBPT, '4'); + console.log(await tx2.wait()); + + console.log(await rewardFaucet.getUpcomingRewardsForNWeeks(bal.address, '4')); + console.log(await rewardFaucet.getUpcomingRewardsForNWeeks(bpt.address, '4')); +} diff --git a/apps/api/scripts/src/veTransfer.ts b/apps/api/scripts/src/veTransfer.ts new file mode 100644 index 000000000..4ea35161a --- /dev/null +++ b/apps/api/scripts/src/veTransfer.ts @@ -0,0 +1,19 @@ +import { ethers } from 'ethers'; +import { PRIVATE_KEY } from '@thxnetwork/api/config/secrets'; +import { getArtifact, contractNetworks } from '@thxnetwork/api/hardhat'; +import { ChainId } from '@thxnetwork/common/enums'; +import { parseUnits } from 'ethers/lib/utils'; + +export default async function main() { + const HARDHAT_RPC = 'http://127.0.0.1:8545/'; + const hardhatProvider = new ethers.providers.JsonRpcProvider(HARDHAT_RPC); + // const TO = '0x9013ae40FCd95D46BA4902F3974A71C40793680B'; + const TO = '0xaf9d56684466fcFcEA0a2B7fC137AB864d642946'; + + const AMOUNT = parseUnits('10000').toString(); + const chainId = ChainId.Hardhat; + const signer = new ethers.Wallet(PRIVATE_KEY, hardhatProvider) as unknown as ethers.Signer; + const bpt = new ethers.Contract(contractNetworks[chainId].BPT, getArtifact('BPT').abi, signer); + + await bpt.transfer(TO, AMOUNT); +} diff --git a/apps/api/scripts/upgradeContractsToLatest.ts b/apps/api/scripts/upgradeContractsToLatest.ts new file mode 100644 index 000000000..ecc0bdb7d --- /dev/null +++ b/apps/api/scripts/upgradeContractsToLatest.ts @@ -0,0 +1,20 @@ +import db from '../src/app/util/database'; +import { MONGODB_URI } from '../src/app/config/secrets'; + +db.connect(MONGODB_URI); + +async function main() { + const startTime = Date.now(); + // Add prerelease jobs here + // ... + + const endTime = Date.now(); + console.log(`🔔 Duration: ${Math.floor((endTime - startTime) / 1000)} seconds`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/apps/api/sonar-project.properties b/apps/api/sonar-project.properties new file mode 100644 index 000000000..ffea24075 --- /dev/null +++ b/apps/api/sonar-project.properties @@ -0,0 +1,6 @@ +sonar.sources=. +sonar.tests="src" +sonar.test.inclusions="src/**/*.test.ts" +sonar.projectKey=thxnetwork_api +sonar.organization=thxnetwork +sonar.javascript.lcov.reportPaths=../../coverage/apps/api/lcov.info diff --git a/apps/api/src/app/config/migrate-mongo-create-only.json b/apps/api/src/app/config/migrate-mongo-create-only.json new file mode 100644 index 000000000..f6776da66 --- /dev/null +++ b/apps/api/src/app/config/migrate-mongo-create-only.json @@ -0,0 +1,4 @@ +{ + "migrationsDir": "src/app/migrations", + "moduleSystem": "commonjs" +} diff --git a/apps/api/src/app/config/migrate-mongo.ts b/apps/api/src/app/config/migrate-mongo.ts new file mode 100644 index 000000000..d98ee850c --- /dev/null +++ b/apps/api/src/app/config/migrate-mongo.ts @@ -0,0 +1,13 @@ +import path from 'path'; +import { MONGODB_URI } from '@thxnetwork/api/config/secrets'; + +export default { + migrationFileExtension: '.js', + mongodb: { + url: MONGODB_URI, + }, + migrationsDir: path.join(path.resolve(__dirname), 'app/migrations'), + changelogCollectionName: 'changelog', + useFileHash: false, + moduleSystem: 'commonjs', +}; diff --git a/apps/api/src/app/config/secrets.ts b/apps/api/src/app/config/secrets.ts new file mode 100644 index 000000000..cfd49366f --- /dev/null +++ b/apps/api/src/app/config/secrets.ts @@ -0,0 +1,121 @@ +import { Speed } from '@openzeppelin/defender-relay-client'; +import path from 'path'; + +const required = [ + 'AUTH_URL', + 'API_URL', + 'DASHBOARD_URL', + 'MONGODB_URI', + 'PORT', + 'AUTH_CLIENT_ID', + 'AUTH_CLIENT_SECRET', + 'INITIAL_ACCESS_TOKEN', + 'AWS_ACCESS_KEY_ID', + 'AWS_SECRET_ACCESS_KEY', + 'AWS_S3_PUBLIC_BUCKET_NAME', + 'AWS_S3_PUBLIC_BUCKET_REGION', + 'AWS_S3_PRIVATE_BUCKET_NAME', + 'AWS_S3_PRIVATE_BUCKET_REGION', + 'SAFE_TXS_SERVICE', + 'CWD', +]; + +if (process.env.NODE_ENV === 'production') { + required.push( + ...[ + 'GCLOUD_RECAPTCHA_API_KEY', + 'POLYGON_RPC', + 'POLYGON_NAME', + 'POLYGON_RELAYER', + 'POLYGON_RELAYER_API_KEY', + 'POLYGON_RELAYER_API_SECRET', + 'INFURA_IPFS_PROJECT_ID', + 'INFURA_IPFS_PROJECT_SECRET', + 'RELAYER_SPEED', + 'TWITTER_API_TOKEN', + ], + ); +} else if (process.env.NODE_ENV === 'development') { + required.push(...['PRIVATE_KEY', 'HARDHAT_RPC', 'LOCAL_CERT', 'LOCAL_CERT_KEY', 'TWITTER_API_TOKEN']); +} + +required.forEach((value: string) => { + if (!process.env[value]) { + console.log(`Set ${value} environment variable.`); + process.exit(1); + } +}); + +// This allows you to use a single .env file with both regular and test configuration. This allows for an +// easy to use setup locally without having hardcoded credentials during test runs. +if (process.env.NODE_ENV === 'test') { + if (process.env.MONGODB_URI_TEST_OVERRIDE !== undefined) + process.env.MONGODB_URI = process.env.MONGODB_URI_TEST_OVERRIDE; + if (process.env.HARDHAT_RPC_TEST_OVERRIDE) process.env.HARDHAT_RPC = process.env.HARDHAT_RPC_TEST_OVERRIDE; +} + +export const VERSION = 'v1'; +export const CWD = process.env.CWD || path.resolve(__dirname, '../../../apps/api/src'); +export const NODE_ENV = process.env.NODE_ENV || 'development'; +export const AUTH_URL = process.env.AUTH_URL || ''; +export const API_URL = process.env.API_URL || ''; +export const WALLET_URL = process.env.WALLET_URL || ''; +export const DASHBOARD_URL = process.env.DASHBOARD_URL || ''; +export const WIDGET_URL = process.env.WIDGET_URL || ''; +export const PUBLIC_URL = process.env.PUBLIC_URL || ''; +export const HARDHAT_RPC = process.env.HARDHAT_RPC || ''; +export const POLYGON_RPC = process.env.POLYGON_RPC || 'https://rpc.ankr.com/polygon'; +export const ETHEREUM_RPC = process.env.ETHEREUM_RPC || 'https://rpc.ankr.com/eth'; +export const MONGODB_URI = String(process.env.MONGODB_URI) || ''; +export const PRIVATE_KEY = process.env.PRIVATE_KEY || ''; +export const PORT = process.env.PORT || ''; +export const AUTH_CLIENT_ID = process.env.AUTH_CLIENT_ID || ''; +export const AUTH_CLIENT_SECRET = process.env.AUTH_CLIENT_SECRET || ''; +export const RATE_LIMIT_REWARD_GIVE = Number(process.env.RATE_LIMIT_REWARD_GIVE) || ''; +export const RATE_LIMIT_REWARD_CLAIM = Number(process.env.RATE_LIMIT_REWARD_CLAIM) || ''; +export const RATE_LIMIT_REWARD_GIVE_WINDOW = Number(process.env.RATE_LIMIT_REWARD_GIVE_WINDOW) || ''; +export const RATE_LIMIT_REWARD_CLAIM_WINDOW = Number(process.env.RATE_LIMIT_REWARD_CLAIM_WINDOW) || ''; +export const INITIAL_ACCESS_TOKEN = process.env.INITIAL_ACCESS_TOKEN || ''; +export const CIRCULATING_SUPPLY = process.env.CIRCULATING_SUPPLY || ''; +export const INFURA_PROJECT_ID = process.env.INFURA_PROJECT_ID || ''; +export const INFURA_IPFS_PROJECT_ID = process.env.INFURA_IPFS_PROJECT_ID || ''; +export const INFURA_IPFS_PROJECT_SECRET = process.env.INFURA_IPFS_PROJECT_SECRET || ''; +export const MINIMUM_GAS_LIMIT = 54680 || ''; +export const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID || ''; +export const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY || ''; +export const AWS_S3_PUBLIC_BUCKET_NAME = process.env.AWS_S3_PUBLIC_BUCKET_NAME || ''; +export const AWS_S3_PUBLIC_BUCKET_REGION = process.env.AWS_S3_PUBLIC_BUCKET_REGION || ''; +export const AWS_S3_PRIVATE_BUCKET_NAME = process.env.AWS_S3_PRIVATE_BUCKET_NAME || ''; +export const AWS_S3_PRIVATE_BUCKET_REGION = process.env.AWS_S3_PRIVATE_BUCKET_REGION || ''; +export const POLYGON_RELAYER = process.env.POLYGON_RELAYER || ''; +export const POLYGON_RELAYER_API_KEY = process.env.POLYGON_RELAYER_API_KEY || ''; +export const POLYGON_RELAYER_API_SECRET = process.env.POLYGON_RELAYER_API_SECRET || ''; +export const LOCAL_CERT = process.env.LOCAL_CERT || ''; +export const LOCAL_CERT_KEY = process.env.LOCAL_CERT_KEY || ''; +export const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000'; +export const RELAYER_SPEED = (process.env.RELAYER_SPEED || 'fastest') as Speed; +export const MIXPANEL_TOKEN = process.env.MIXPANEL_TOKEN || ''; +export const MIXPANEL_API_URL = 'https://api.mixpanel.com'; +export const CYPRESS_EMAIL = process.env.CYPRESS_EMAIL || 'cypress@thx.network'; +export const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY || ''; +export const STRIPE_SECRET_WEBHOOK = process.env.STRIPE_SECRET_WEBHOOK || ''; +export const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY || ''; +export const TWITTER_API_TOKEN = process.env.TWITTER_API_TOKEN || ''; +export const IPFS_BASE_URL = 'https://ipfs.io/ipfs/'; +export const WEBHOOK_REFERRAL = process.env.WEBHOOK_REFERRAL || ''; +export const WEBHOOK_MILESTONE = process.env.WEBHOOK_MILESTONE || ''; +export const SAFE_TXS_SERVICE = process.env.SAFE_TXS_SERVICE || 'https://safe-transaction-polygon.safe.global'; +export const BOT_TOKEN = process.env.BOT_TOKEN || ''; +export const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID || ''; +export const GITCOIN_API_KEY = process.env.GITCOIN_API_KEY || ''; +export const BALANCER_POOL_ID = '0xb204bf10bc3a5435017d3db247f56da601dfe08a0002000000000000000000fe'; +export const PINATA_API_JWT = process.env.PINATA_API_JWT || ''; +export const ALLOWED_API_CLIENT_ID = process.env.ALLOWED_API_CLIENT_ID || ''; +export const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || ''; +export const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET || ''; +export const GCLOUD_PROJECT_ID = process.env.GCLOUD_PROJECT_ID || ''; +export const GCLOUD_RECAPTCHA_API_KEY = process.env.GCLOUD_RECAPTCHA_API_KEY || ''; +export const GCLOUD_RECAPTCHA_SITE_KEY = process.env.GCLOUD_RECAPTCHA_SITE_KEY || ''; +export const THX_CLIENT_ID = process.env.THX_CLIENT_ID || ''; +export const THX_CLIENT_SECRET = process.env.THX_CLIENT_SECRET || ''; +export const WEBHOOK_SIGNING_SECRET = process.env.WEBHOOK_SIGNING_SECRET || ''; diff --git a/apps/api/src/app/connection-profiles/cpp-curator.json b/apps/api/src/app/connection-profiles/cpp-curator.json new file mode 100644 index 000000000..36719936d --- /dev/null +++ b/apps/api/src/app/connection-profiles/cpp-curator.json @@ -0,0 +1,37 @@ +{ + "name": "test-network-CuratorOrg", + "version": "1.0.0", + "client": { + "organization": "CuratorOrg" + }, + "organizations": { + "CuratorOrg": { + "mspid": "CuratorOrg", + "peers": ["peer0.curator.local"], + "certificateAuthorities": ["ca.curator.local"] + } + }, + "peers": { + "peer0.curator.local": { + "url": "grpcs://localhost:7041", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/curator.local/peers/peer0.curator.local/tls/ca.crt" + }, + "grpcOptions": { + "ssl-target-name-override": "peer0.curator.local" + } + } + }, + "certificateAuthorities": { + "ca.curator.local": { + "url": "https://localhost:7040", + "caName": "ca.curator.local", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/curator.local/peers/ca.curator.local/tls/ca.crt" + }, + "httpOptions": { + "verify": false + } + } + } +} diff --git a/apps/api/src/app/connection-profiles/cpp-partner.json b/apps/api/src/app/connection-profiles/cpp-partner.json new file mode 100644 index 000000000..4afcd6ebc --- /dev/null +++ b/apps/api/src/app/connection-profiles/cpp-partner.json @@ -0,0 +1,41 @@ +{ + "name": "test-network-PartnerOrg1", + "version": "1.0.0", + "client": { + "organization": "PartnerOrg1" + }, + "organizations": { + "PartnerOrg1": { + "mspid": "PartnerOrg1", + "peers": [ + "peer0.partner1.local" + ], + "certificateAuthorities": [ + "ca.partner1.local" + ] + } + }, + "peers": { + "peer0.partner1.local": { + "url": "grpcs://localhost:7061", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/partner1.local/peers/peer0.partner1.local/tls/ca.crt" + }, + "grpcOptions": { + "ssl-target-name-override": "peer0.partner1.local" + } + } + }, + "certificateAuthorities": { + "ca.partner1.local": { + "url": "https://localhost:7060", + "caName": "ca.partner1.local", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/partner1.local/peers/ca.partner1.local/tls/ca.crt" + }, + "httpOptions": { + "verify": false + } + } + } +} \ No newline at end of file diff --git a/apps/api/src/app/connection-profiles/cpp-users.json b/apps/api/src/app/connection-profiles/cpp-users.json new file mode 100644 index 000000000..f95a21cca --- /dev/null +++ b/apps/api/src/app/connection-profiles/cpp-users.json @@ -0,0 +1,59 @@ +{ + "name": "test-network-UsersOrg1", + "version": "1.0.0", + "client": { + "organization": "UsersOrg1" + }, + "organizations": { + "CuratorOrg": { + "mspid": "CuratorOrg", + "peers": [ + "peer0.curator.local" + ] + }, + "PartnerOrg1": { + "mspid": "PartnerOrg1", + "peers": [ + "peer0.partner1.local" + ] + }, + "UsersOrg1": { + "mspid": "UsersOrg1", + "certificateAuthorities": [ + "ca.users1.local" + ] + } + }, + "peers": { + "peer0.curator.local": { + "url": "grpcs://localhost:7041", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/curator.local/peers/peer0.curator.local/tls/ca.crt" + }, + "grpcOptions": { + "ssl-target-name-override": "peer0.curator.local" + } + }, + "peer0.partner1.local": { + "url": "grpcs://localhost:7061", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/partner1.local/peers/peer0.partner1.local/tls/ca.crt" + }, + "grpcOptions": { + "ssl-target-name-override": "peer0.partner1.local" + } + } + }, + "certificateAuthorities": { + "ca.users1.local": { + "url": "https://localhost:7080", + "caName": "ca.users1.local", + "tlsCACerts": { + "path": "/Users/peterpolman/Sites/galachain/test-network/fablo-target/fabric-config/crypto-config/peerOrganizations/users1.local/peers/ca.users1.local/tls/ca.crt" + }, + "httpOptions": { + "verify": false + } + } + } +} \ No newline at end of file diff --git a/apps/api/src/app/controllers/account/account.router.ts b/apps/api/src/app/controllers/account/account.router.ts new file mode 100644 index 000000000..003598405 --- /dev/null +++ b/apps/api/src/app/controllers/account/account.router.ts @@ -0,0 +1,68 @@ +import express from 'express'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +import RouterWallet from './wallet/wallets.router'; + +// Account +import * as ReadAccount from './get.controller'; +import * as UpdateAccount from './patch.controller'; +import * as DeleteAccount from './delete.controller'; + +// Social OAuth +import * as CreateAccountDisconnect from './disconnect/post.controller'; + +import * as ReadAccountDiscord from './discord/get.controller'; +import * as GetAccountByDiscordId from './discord/get.by-discord-id.controller'; +import * as CreateTwitterTweet from './twitter/tweet/post.controller'; +import * as CreateTwitterUser from './twitter/user/post.controller'; +import * as CreateTwitterSearch from './twitter/search/post.controller'; +import * as CreateTwitterUserByUsername from './twitter/user/by/username/post.controller'; + +const router: express.Router = express.Router(); + +router.use('/wallets', RouterWallet); + +router.get('/', guard.check(['account:read']), ReadAccount.controller); +router.patch('/', guard.check(['account:read', 'account:write']), UpdateAccount.controller); +router.delete('/', guard.check(['account:write']), DeleteAccount.controller); + +router.post( + '/disconnect', + guard.check(['account:read', 'account:write']), + assertRequestInput(CreateAccountDisconnect.validation), + CreateAccountDisconnect.controller, +); + +router.get('/discord', guard.check(['account:read']), ReadAccountDiscord.controller); +router.get( + '/discord/:discordId', + guard.check(['account:read']), + assertRequestInput(GetAccountByDiscordId.validation), + GetAccountByDiscordId.controller, +); +router.post( + '/twitter/tweet', + assertRequestInput(CreateTwitterTweet.validation), + guard.check(['account:read']), + CreateTwitterTweet.controller, +); +router.post( + '/twitter/user/', + assertRequestInput(CreateTwitterUser.validation), + guard.check(['account:read']), + CreateTwitterUser.controller, +); +router.post( + '/twitter/search/', + assertRequestInput(CreateTwitterSearch.validation), + guard.check(['account:read']), + CreateTwitterSearch.controller, +); +router.post( + '/twitter/user/by/username', + assertRequestInput(CreateTwitterUserByUsername.validation), + guard.check(['account:read']), + CreateTwitterUserByUsername.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/account/delete.controller.ts b/apps/api/src/app/controllers/account/delete.controller.ts new file mode 100644 index 000000000..7a051c137 --- /dev/null +++ b/apps/api/src/app/controllers/account/delete.controller.ts @@ -0,0 +1,9 @@ +import { Response, Request } from 'express'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const controller = async (req: Request, res: Response) => { + await AccountProxy.remove(req.auth.sub); + + res.status(204).end(); +}; +export { controller }; diff --git a/apps/api/src/app/controllers/account/disconnect/post.controller.ts b/apps/api/src/app/controllers/account/disconnect/post.controller.ts new file mode 100644 index 000000000..6b69a1810 --- /dev/null +++ b/apps/api/src/app/controllers/account/disconnect/post.controller.ts @@ -0,0 +1,13 @@ +import { Response, Request } from 'express'; +import { body } from 'express-validator'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const validation = [body('kind').isString()]; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + await AccountProxy.disconnect(account, req.body.kind); + + res.end(); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/discord/get.by-discord-id.controller.ts b/apps/api/src/app/controllers/account/discord/get.by-discord-id.controller.ts new file mode 100644 index 000000000..5be5f6a9f --- /dev/null +++ b/apps/api/src/app/controllers/account/discord/get.by-discord-id.controller.ts @@ -0,0 +1,15 @@ +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Wallet } from '@thxnetwork/api/models/Wallet'; + +const validation = [param('discordId')]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Account'] + const account = await AccountProxy.getByDiscordId(req.params.discordId); + const wallets = await Wallet.find({ sub: account.sub }); + + res.json({ account, wallets }); +}; +export { validation, controller }; diff --git a/apps/api/src/app/controllers/account/discord/get.controller.ts b/apps/api/src/app/controllers/account/discord/get.controller.ts new file mode 100644 index 000000000..52c260ea4 --- /dev/null +++ b/apps/api/src/app/controllers/account/discord/get.controller.ts @@ -0,0 +1,19 @@ +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import DiscordDataProxy from '@thxnetwork/api/proxies/DiscordDataProxy'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { AccessTokenKind, OAuthDiscordScope } from '@thxnetwork/common/enums'; +import { Request, Response } from 'express'; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + const token = await AccountProxy.getToken(account, AccessTokenKind.Discord, [ + OAuthDiscordScope.Identify, + OAuthDiscordScope.Guilds, + ]); + if (!token) throw new NotFoundError('Discord token not found.'); + + const guilds = await DiscordDataProxy.getGuilds(token); + + res.json({ guilds }); +}; +export { controller }; diff --git a/apps/api/src/app/controllers/account/get.controller.ts b/apps/api/src/app/controllers/account/get.controller.ts new file mode 100644 index 000000000..82c5ae188 --- /dev/null +++ b/apps/api/src/app/controllers/account/get.controller.ts @@ -0,0 +1,39 @@ +import { Request, Response } from 'express'; +import { WalletVariant, AccountVariant } from '@thxnetwork/common/enums'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import WalletService from '@thxnetwork/api/services/WalletService'; +import THXService from '@thxnetwork/api/services/THXService'; +import ContractService from '@thxnetwork/api/services/ContractService'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + + // Connect identity if none exists + await THXService.connect(account); + + // Remove actual tokens from response + account.tokens = account.tokens.map(({ kind, userId, scopes, metadata }) => ({ + kind, + userId, + scopes, + metadata, + })) as TToken[]; + + // If account variant is metamask and no wallet is found then create it + if (account.variant === AccountVariant.Metamask) { + const wallet = await WalletService.findOne({ sub: req.auth.sub, variant: WalletVariant.WalletConnect }); + if (!wallet) { + await WalletService.createWalletConnect({ + sub: req.auth.sub, + address: account.address, + chainId: ContractService.getChainId(), + }); + } + } + + res.json(account); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/patch.controller.ts b/apps/api/src/app/controllers/account/patch.controller.ts new file mode 100644 index 000000000..d9804e943 --- /dev/null +++ b/apps/api/src/app/controllers/account/patch.controller.ts @@ -0,0 +1,9 @@ +import { Request, Response } from 'express'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.update(req.auth.sub, req.body); + res.json(account); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/account/twitter/search/post.controller.ts b/apps/api/src/app/controllers/account/twitter/search/post.controller.ts new file mode 100644 index 000000000..91cb4f4cc --- /dev/null +++ b/apps/api/src/app/controllers/account/twitter/search/post.controller.ts @@ -0,0 +1,17 @@ +import { TwitterQuery } from '@thxnetwork/common/twitter'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import TwitterDataProxy from '@thxnetwork/api/proxies/TwitterDataProxy'; + +const validation = [body('operators').customSanitizer((ops) => TwitterQuery.parse(ops))]; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + const query = TwitterQuery.create(req.body.operators); + const posts = await TwitterDataProxy.search(account, query); + + res.json(posts); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/twitter/tweet/post.controller.ts b/apps/api/src/app/controllers/account/twitter/tweet/post.controller.ts new file mode 100644 index 000000000..0a18d7fe1 --- /dev/null +++ b/apps/api/src/app/controllers/account/twitter/tweet/post.controller.ts @@ -0,0 +1,15 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import TwitterDataProxy from '@thxnetwork/api/proxies/TwitterDataProxy'; + +const validation = [body('tweetId').isString()]; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + const tweet = await TwitterDataProxy.getTweet(account, req.body.tweetId); + + res.json(tweet); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/twitter/user/by/username/post.controller.ts b/apps/api/src/app/controllers/account/twitter/user/by/username/post.controller.ts new file mode 100644 index 000000000..54c11fe52 --- /dev/null +++ b/apps/api/src/app/controllers/account/twitter/user/by/username/post.controller.ts @@ -0,0 +1,15 @@ +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import TwitterDataProxy from '@thxnetwork/api/proxies/TwitterDataProxy'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; + +const validation = [body('username').isString()]; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + const user = await TwitterDataProxy.getUserByUsername(account, req.body.username); + + res.json(user); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/twitter/user/post.controller.ts b/apps/api/src/app/controllers/account/twitter/user/post.controller.ts new file mode 100644 index 000000000..56149d6f0 --- /dev/null +++ b/apps/api/src/app/controllers/account/twitter/user/post.controller.ts @@ -0,0 +1,15 @@ +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import TwitterDataProxy from '@thxnetwork/api/proxies/TwitterDataProxy'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; + +const validation = [body('userId').isString()]; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + const user = await TwitterDataProxy.getUser(account, req.body.userId); + + res.json(user); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/wallet/confirm/post.controller.ts b/apps/api/src/app/controllers/account/wallet/confirm/post.controller.ts new file mode 100644 index 000000000..942713c10 --- /dev/null +++ b/apps/api/src/app/controllers/account/wallet/confirm/post.controller.ts @@ -0,0 +1,28 @@ +import { Request, Response } from 'express'; +import { body, query } from 'express-validator'; +import { BadRequestError, ForbiddenError } from '@thxnetwork/api/util/errors'; +import { Transaction } from '@thxnetwork/api/models'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [ + body('chainId').isNumeric(), + body('safeTxHash').isString(), + body('signature').isString(), + query('walletId').isMongoId(), +]; + +const controller = async (req: Request, res: Response) => { + const walletId = req.query.walletId as string; + const wallet = await SafeService.findById(walletId); + if (!wallet) throw new BadRequestError('Wallet not found.'); + if (wallet.sub !== req.auth.sub) throw new ForbiddenError('Wallet not owned by sub.'); + + const tx = await Transaction.findOne({ safeTxHash: req.body.safeTxHash }); + if (!tx) throw new BadRequestError('Transaction not found.'); + + await SafeService.confirm(wallet, req.body.safeTxHash, req.body.signature); + + res.json(wallet); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/wallet/list.controller.ts b/apps/api/src/app/controllers/account/wallet/list.controller.ts new file mode 100644 index 000000000..5727a0583 --- /dev/null +++ b/apps/api/src/app/controllers/account/wallet/list.controller.ts @@ -0,0 +1,15 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import WalletService from '@thxnetwork/api/services/WalletService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const validation = [query('chainId').optional().isNumeric()]; + +const controller = async (req: Request, res: Response) => { + const account = await AccountProxy.findById(req.auth.sub); + const wallets = await WalletService.list(account); + + res.json(wallets); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/wallet/post.controller.ts b/apps/api/src/app/controllers/account/wallet/post.controller.ts new file mode 100644 index 000000000..f34782890 --- /dev/null +++ b/apps/api/src/app/controllers/account/wallet/post.controller.ts @@ -0,0 +1,26 @@ +import { Request, Response } from 'express'; +import { recoverSigner } from '@thxnetwork/api/util/network'; +import { body } from 'express-validator'; +import WalletService from '@thxnetwork/api/services/WalletService'; + +const validation = [ + body('variant').isString(), + body('message').optional().isString(), + body('signature').optional().isString(), +]; + +const controller = async (req: Request, res: Response) => { + const { message, signature, variant } = req.body; + const data: Partial<TWallet> = { sub: req.auth.sub }; + + // If no message and signature are present prepare a wallet to connect later + if (signature && message) { + data.address = recoverSigner(message, signature); + } + + await WalletService.create(variant, data); + + res.status(201).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/account/wallet/wallets.router.ts b/apps/api/src/app/controllers/account/wallet/wallets.router.ts new file mode 100644 index 000000000..e07041a04 --- /dev/null +++ b/apps/api/src/app/controllers/account/wallet/wallets.router.ts @@ -0,0 +1,23 @@ +import express from 'express'; +import * as ListWallets from './list.controller'; +import * as CreateWallets from './post.controller'; +import * as CreateWalletConfirm from './confirm/post.controller'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', guard.check(['account:read']), assertRequestInput(ListWallets.validation), ListWallets.controller); +router.post( + '/', + guard.check(['account:read', 'account:write']), + assertRequestInput(CreateWallets.validation), + CreateWallets.controller, +); +router.post( + '/confirm', + guard.check(['account:read', 'account:write']), + assertRequestInput(CreateWalletConfirm.validation), + CreateWalletConfirm.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/account/wallet/wallets.test.ts b/apps/api/src/app/controllers/account/wallet/wallets.test.ts new file mode 100644 index 000000000..983a9186d --- /dev/null +++ b/apps/api/src/app/controllers/account/wallet/wallets.test.ts @@ -0,0 +1,86 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { widgetAccessToken, sub, userWalletPrivateKey4 } from '@thxnetwork/api/util/jest/constants'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { ChainId, WalletVariant } from '@thxnetwork/common/enums'; +import { signMessage } from '@thxnetwork/api/util/jest/network'; +import { safeVersion } from '@thxnetwork/api/services/ContractService'; + +const user = request.agent(app); + +describe('Account Wallets', () => { + beforeAll(() => beforeAllCallback({ skipWalletCreation: true })); + afterAll(afterAllCallback); + + describe('GET /wallets', () => { + it('HTTP 200 if OK', (done) => { + user.get(`/v1/account/wallets`) + .set({ Authorization: widgetAccessToken }) + .expect((res: request.Response) => { + expect(res.body.length).toEqual(0); + }) + .expect(200, done); + }); + }); + + // Create Safe Wallet + describe('POST /wallets (Safe)', () => { + it('HTTP 200 if OK', (done) => { + const message = 'test'; + const signature = signMessage(userWalletPrivateKey4, message); + user.post(`/v1/account/wallets`) + .set({ Authorization: widgetAccessToken }) + .send({ + variant: WalletVariant.Safe, + message, + signature, + }) + .expect(201, done); + }); + }); + + describe('POST /wallets (WalletConnect)', () => { + it('HTTP 200 if OK', (done) => { + const message = 'test'; + const signature = signMessage(userWalletPrivateKey4, message); + user.post(`/v1/account/wallets`) + .set({ Authorization: widgetAccessToken }) + .send({ + variant: WalletVariant.WalletConnect, + message, + signature, + }) + .expect(201, done); + }); + }); + + // Create WebConnect wallet + + describe('GET /wallets', () => { + it('HTTP 200 if OK', (done) => { + user.get('/v1/account/wallets') + .set({ Authorization: widgetAccessToken }) + .expect((res: request.Response) => { + expect(res.body.length).toEqual(2); + + const safe = res.body.find((wallet: any) => wallet.variant === WalletVariant.Safe); + const wallet = res.body.find((wallet: any) => wallet.variant === WalletVariant.WalletConnect); + expect(safe.sub).toEqual(sub); + expect(safe.chainId).toEqual(ChainId.Hardhat); + expect(safe.variant).toBe(WalletVariant.Safe); + expect(safe.address).toBeDefined(); + expect(safe.safeVersion).toBe(safeVersion); + + expect(wallet.sub).toEqual(sub); + expect(wallet.chainId).toEqual(ChainId.Hardhat); + expect(wallet.variant).toBe(WalletVariant.WalletConnect); + expect(wallet.address).toBeDefined(); + }) + .expect(200, done); + }); + }); + + describe('POST /wallets', () => { + // + }); +}); diff --git a/apps/api/src/app/controllers/brands/brands.router.ts b/apps/api/src/app/controllers/brands/brands.router.ts new file mode 100644 index 000000000..5f46aaca6 --- /dev/null +++ b/apps/api/src/app/controllers/brands/brands.router.ts @@ -0,0 +1,20 @@ +import express from 'express'; +import { assertRequestInput, checkJwt, corsHandler, guard } from '@thxnetwork/api/middlewares'; + +import * as GetBrand from './get.controller'; +import * as PutBrand from './put.controller'; + +const router: express.Router = express.Router(); +router.get('/', GetBrand.controller); + +router + .use(checkJwt) + .use(corsHandler) + .put( + '/', + guard.check(['brands:write', 'brands:read']), + assertRequestInput(PutBrand.validation), + PutBrand.controller, + ); + +export default router; diff --git a/apps/api/src/app/controllers/brands/get.controller.ts b/apps/api/src/app/controllers/brands/get.controller.ts new file mode 100644 index 000000000..24452b312 --- /dev/null +++ b/apps/api/src/app/controllers/brands/get.controller.ts @@ -0,0 +1,9 @@ +import { Request, Response } from 'express'; +import BrandService from '@thxnetwork/api/services/BrandService'; + +const controller = async (req: Request, res: Response) => { + const brand = await BrandService.get(req.header('X-PoolId')); + res.json(brand); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/brands/put.controller.ts b/apps/api/src/app/controllers/brands/put.controller.ts new file mode 100644 index 000000000..2bf9a077b --- /dev/null +++ b/apps/api/src/app/controllers/brands/put.controller.ts @@ -0,0 +1,34 @@ +import { isValidUrl } from '@thxnetwork/api/util/url'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import BrandService from '../../services/BrandService'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import CanvasService from '@thxnetwork/api/services/CanvasService'; +import ImageService from '@thxnetwork/api/services/ImageService'; + +const validation = [ + body('logoImgUrl').custom((logoImgUrl?: string) => { + return logoImgUrl && logoImgUrl.length ? isValidUrl(logoImgUrl) : true; + }), + body('backgroundImgUrl').custom((backgroundImgUrl?: string) => { + return backgroundImgUrl && backgroundImgUrl.length ? isValidUrl(backgroundImgUrl) : true; + }), +]; +const controller = async (req: Request, res: Response) => { + const poolId = req.header('X-PoolId'); + const hasAccess = await PoolService.isSubjectAllowed(req.auth.sub, poolId); + if (!hasAccess) throw new ForbiddenError('Not your pool'); + + // Update logo and bg changes + const { logoImgUrl, backgroundImgUrl } = req.body; + const brand = await BrandService.update({ poolId }, { logoImgUrl, backgroundImgUrl }); + + // Create campaign preview + const previewFile = await CanvasService.createPreviewImage(brand); + brand.previewImgUrl = await ImageService.uploadToS3(previewFile, `${poolId}_campaign_preview.png`, 'image/*'); + + res.json(await brand.save()); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/client/client.router.ts b/apps/api/src/app/controllers/client/client.router.ts new file mode 100644 index 000000000..791575cd0 --- /dev/null +++ b/apps/api/src/app/controllers/client/client.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import * as ListController from './list.controller'; +import * as PostController from './post.controller'; +import * as PatchController from './patch.controller'; +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router(); + +router.get('/', guard.check(['clients:read']), assertPoolAccess, ListController.controller); +router.patch( + '/:id', + guard.check(['clients:read', 'clients:write']), + assertRequestInput(PatchController.validation), + assertPoolAccess, + PatchController.controller, +); +router.post('/', guard.check(['clients:read', 'clients:write']), assertPoolAccess, PostController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/client/list.controller.ts b/apps/api/src/app/controllers/client/list.controller.ts new file mode 100644 index 000000000..a9c38d26c --- /dev/null +++ b/apps/api/src/app/controllers/client/list.controller.ts @@ -0,0 +1,16 @@ +import { Request, Response } from 'express'; +import { Client } from '@thxnetwork/api/models/Client'; +import ClientProxy from '@thxnetwork/api/proxies/ClientProxy'; + +const controller = async (req: Request, res: Response) => { + const poolId = req.header('X-PoolId'); + const clients = await Client.find({ poolId }); + const promises = clients.map(async (client) => { + return await ClientProxy.getCredentials(client.toJSON()); + }); + const result = await Promise.all(promises); + + res.status(200).json(result); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/client/patch.controller.ts b/apps/api/src/app/controllers/client/patch.controller.ts new file mode 100644 index 000000000..87787cc65 --- /dev/null +++ b/apps/api/src/app/controllers/client/patch.controller.ts @@ -0,0 +1,15 @@ +import ClientProxy from '@thxnetwork/api/proxies/ClientProxy'; +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { body, param } from 'express-validator'; + +const validation = [param('id').exists(), body('name').exists()]; +const controller = async (req: Request, res: Response) => { + let client = await ClientProxy.get(req.params.id); + if (!client) throw new NotFoundError('Cannot found Client ID in this request'); + + client = await ClientProxy.update(client.clientId, { name: req.body['name'] }); + res.status(200).json(client); +}; + +export { validation, controller }; diff --git a/apps/api/src/app/controllers/client/post.controller.ts b/apps/api/src/app/controllers/client/post.controller.ts new file mode 100644 index 000000000..81cc6a8c2 --- /dev/null +++ b/apps/api/src/app/controllers/client/post.controller.ts @@ -0,0 +1,34 @@ +import { Request, Response } from 'express'; +import { GrantVariant } from '@thxnetwork/common/enums'; +import { widgetScopes } from '@thxnetwork/api/util/jest/constants'; +import ClientProxy from '@thxnetwork/api/proxies/ClientProxy'; + +const controller = async (req: Request, res: Response) => { + const poolId = req.header('X-PoolId'); + const { grantType, redirectUri, requestUri, name } = req.body; + const grantMap = { + [GrantVariant.AuthorizationCode]: { + application_type: 'web', + grant_types: [grantType], + request_uris: [requestUri], + redirect_uris: [redirectUri], + post_logout_redirect_uris: [requestUri], + response_types: ['code'], + scope: widgetScopes, + }, + [GrantVariant.ClientCredentials]: { + application_type: 'web', + grant_types: [grantType], + request_uris: [], + redirect_uris: [], + response_types: [], + scope: 'openid events:write identities:read identities:write pools:write pools:read', + }, + }; + const payload = grantMap[grantType]; + const client = await ClientProxy.create(req.auth.sub, poolId, payload, name); + + res.json(client); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/coupons/coupons.router.ts b/apps/api/src/app/controllers/coupons/coupons.router.ts new file mode 100644 index 000000000..75aece92d --- /dev/null +++ b/apps/api/src/app/controllers/coupons/coupons.router.ts @@ -0,0 +1,22 @@ +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import express from 'express'; +import * as ListCouponCode from './list.controller'; +import * as RemoveCouponCode from './delete.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['coupon_rewards:read']), + assertRequestInput(ListCouponCode.validation), + ListCouponCode.controller, +); + +router.delete( + '/:couponCodeId/', + guard.check(['coupon_rewards:write']), + assertRequestInput(RemoveCouponCode.validation), + RemoveCouponCode.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/coupons/delete.controller.ts b/apps/api/src/app/controllers/coupons/delete.controller.ts new file mode 100644 index 000000000..a2f0f93af --- /dev/null +++ b/apps/api/src/app/controllers/coupons/delete.controller.ts @@ -0,0 +1,30 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { CouponCode } from '@thxnetwork/api/models/CouponCode'; +import { RewardCouponPayment } from '@thxnetwork/api/models/RewardCouponPayment'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import RewardService from '@thxnetwork/api/services/RewardService'; + +const validation = [param('couponCodeId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const couponCode = await CouponCode.findById(req.params.couponCodeId); + if (!couponCode) throw new NotFoundError('Coupon code not found'); + + // Check if user is allowed to access the pool for this couponRewardId + const reward = await RewardService.findById(RewardVariant.Coupon, couponCode.couponRewardId); + const isAllowed = await PoolService.isSubjectAllowed(req.auth.sub, reward.poolId); + if (!isAllowed) throw new ForbiddenError('Not allowed to access these coupon codes'); + + // Check if the coupon code has already been purchased + const payment = await RewardCouponPayment.exists({ couponCodeId: req.params.couponCodeId }); + if (payment) throw new ForbiddenError('Coupon code has been redeemed'); + + await CouponCode.findByIdAndDelete(req.params.couponCodeId); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/coupons/list.controller.ts b/apps/api/src/app/controllers/coupons/list.controller.ts new file mode 100644 index 000000000..2136f2717 --- /dev/null +++ b/apps/api/src/app/controllers/coupons/list.controller.ts @@ -0,0 +1,32 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import RewardService from '@thxnetwork/api/services/RewardService'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; + +const validation = [ + query('couponRewardId').isMongoId(), + query('page').isInt(), + query('limit').isInt(), + query('query').isString(), +]; + +const controller = async (req: Request, res: Response) => { + const couponRewardId = req.query.couponRewardId as string; + const page = Number(req.query.page); + const limit = Number(req.query.limit); + const query = req.query.query as string; + console.log(query); + + // Check if user is allowed to access the pool for this couponRewardId + const reward = await RewardService.findById(RewardVariant.Coupon, couponRewardId); + const isAllowed = await PoolService.isSubjectAllowed(req.auth.sub, reward.poolId); + if (!isAllowed) throw new ForbiddenError('Not allowed to access these coupon codes'); + + const result = await PoolService.findCouponCodes({ couponRewardId, query }, page, limit); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/data/data.router.ts b/apps/api/src/app/controllers/data/data.router.ts new file mode 100644 index 000000000..facda5c9a --- /dev/null +++ b/apps/api/src/app/controllers/data/data.router.ts @@ -0,0 +1,35 @@ +import express, { Request, Response } from 'express'; +import axios, { AxiosRequestConfig, Method } from 'axios'; +import { MIXPANEL_API_URL } from '@thxnetwork/api/config/secrets'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +// import { getIP } from '@thxnetwork/api/util/ip'; + +const router: express.Router = express.Router(); + +const mixpanelProxy = function (options: AxiosRequestConfig) { + if (!options.url.startsWith('/')) throw new ForbiddenError(); + axios.defaults.baseURL = MIXPANEL_API_URL; + return axios(options); +}; + +router.all('*', async (req: Request, res: Response) => { + // if (req.body.data) { + // const dataDecoded = Buffer.from(req.body.data, 'base64').toString(); + // const dataObject = JSON.parse(dataDecoded); + // const data = dataObject.map((item) => { + // if (!item || !item.event) return item; + // return { properties: { ...item.properties, real_ip: getIP(req) } }; + // }); + // const dataString = JSON.stringify(data); + // req.body.data = Buffer.from(dataString).toString('base64'); + // } + await mixpanelProxy({ + method: req.method as Method, + url: req.originalUrl.replace(req.baseUrl, ''), + headers: { 'X-REAL-IP': req.ip }, + params: req.body, + }); + res.end(); +}); + +export default router; diff --git a/apps/api/src/app/controllers/earn/earn.router.ts b/apps/api/src/app/controllers/earn/earn.router.ts new file mode 100644 index 000000000..9993a31fd --- /dev/null +++ b/apps/api/src/app/controllers/earn/earn.router.ts @@ -0,0 +1,10 @@ +import express from 'express'; +import * as ListPriceController from './prices/list.controller'; +import * as ListAPRController from './metrics/list.controller'; + +const router: express.Router = express.Router(); + +router.get('/prices', ListPriceController.controller); +router.get('/metrics', ListAPRController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/earn/metrics/list.controller.ts b/apps/api/src/app/controllers/earn/metrics/list.controller.ts new file mode 100644 index 000000000..ec1f02bb6 --- /dev/null +++ b/apps/api/src/app/controllers/earn/metrics/list.controller.ts @@ -0,0 +1,16 @@ +import BalancerService from '@thxnetwork/api/services/BalancerService'; +import WalletService from '@thxnetwork/api/services/WalletService'; +import { ChainId } from '@thxnetwork/common/enums'; +import { Request, Response } from 'express'; +import { query } from 'express-validator'; + +const validation = [query('walletId').optional().isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const wallet = await WalletService.findById(req.query.walletId as string); + const result = BalancerService.getMetrics(wallet ? wallet.chainId : ChainId.Polygon); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/earn/prices/list.controller.ts b/apps/api/src/app/controllers/earn/prices/list.controller.ts new file mode 100644 index 000000000..28304a0d0 --- /dev/null +++ b/apps/api/src/app/controllers/earn/prices/list.controller.ts @@ -0,0 +1,9 @@ +import BalancerService from '@thxnetwork/api/services/BalancerService'; +import { Request, Response } from 'express'; + +const controller = async (req: Request, res: Response) => { + const pricing = BalancerService.getPricing(); + res.json(pricing); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/erc1155/delete.controller.ts b/apps/api/src/app/controllers/erc1155/delete.controller.ts new file mode 100644 index 000000000..bb6a20f6d --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/delete.controller.ts @@ -0,0 +1,16 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { ERC1155 } from '@thxnetwork/api/models/ERC1155'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const erc1155 = await ERC1155.findById(req.params.id); + if (erc1155.sub !== req.auth.sub) throw new ForbiddenError('Not your ERC1155'); + + await erc1155.deleteOne(); + + return res.status(204).end(); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/erc1155.router.ts b/apps/api/src/app/controllers/erc1155/erc1155.router.ts new file mode 100644 index 000000000..90c2f465c --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/erc1155.router.ts @@ -0,0 +1,99 @@ +import express from 'express'; +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import { upload } from '@thxnetwork/api/util/multer'; + +import * as ReadERC1155 from './get.controller'; +import * as ListERC1155 from './list.controller'; +import * as RemoveERC1155 from './delete.controller'; +import * as ListERC1155Metadata from './metadata/list.controller'; +import * as ListERC1155Token from './token/list.controller'; +import * as ReadERC1155Token from './token/get.controller'; +import * as CreateERC1155 from './post.controller'; +import * as CreateERC1155Metadata from './metadata/post.controller'; + +import * as UpdateERC1155 from './patch.controller'; +import * as ReadERC1155Metadata from './metadata/get.controller'; +import * as PatchERC1155Metadata from './metadata/patch.controller'; +import * as DeleteERC1155Metadata from './metadata/delete.controller'; +import * as ImportERC1155Contract from './import/post.controller'; +import * as PreviewERC1155Contract from './import/preview/post.controller'; +import * as CreateERC1155Transfer from './transfer/post.controller'; + +const router: express.Router = express.Router(); + +router.get( + '/token', + guard.check(['erc1155:read']), + assertRequestInput(ListERC1155Token.validation), + ListERC1155Token.controller, +); +router.get('/token/:id', guard.check(['erc1155:read']), ReadERC1155Token.controller); +router.get('/', guard.check(['erc1155:read']), assertRequestInput(ListERC1155.validation), ListERC1155.controller); +router.get('/:id', guard.check(['erc1155:read']), assertRequestInput(ReadERC1155.validation), ReadERC1155.controller); + +router.post( + '/', + upload.single('file'), + guard.check(['erc1155:read', 'erc1155:write']), + assertRequestInput(CreateERC1155.validation), + CreateERC1155.controller, +); +router.post( + '/import', + ImportERC1155Contract.controller, + assertPoolAccess, + assertRequestInput(ImportERC1155Contract.validation), +); +router.post('/preview', assertRequestInput(PreviewERC1155Contract.validation), PreviewERC1155Contract.controller); +router.patch( + '/:id/metadata/:metadataId', + guard.check(['erc1155:write']), + assertRequestInput(PatchERC1155Metadata.validation), + PatchERC1155Metadata.controller, +); + +router.delete( + '/:id/metadata/:metadataId', + guard.check(['erc1155:write']), + assertRequestInput(DeleteERC1155Metadata.validation), + DeleteERC1155Metadata.controller, +); + +router.get('/:id/metadata', guard.check(['erc1155:read']), ListERC1155Metadata.controller); + +router.post( + '/:id/metadata/', + guard.check(['erc1155:write']), + assertRequestInput(CreateERC1155Metadata.validation), + CreateERC1155Metadata.controller, +); + +router.patch( + '/:id', + guard.check(['erc1155:write', 'erc1155:read']), + assertRequestInput(UpdateERC1155.validation), + UpdateERC1155.controller, +); + +router.get( + '/:id/metadata/:metadataId', + guard.check(['erc1155:read']), + ReadERC1155Metadata.controller, + assertRequestInput(ReadERC1155Metadata.validation), +); + +router.post( + '/transfer', + // guard.check(['erc1155_transfer:read', 'erc1155_transfer:write']), + assertRequestInput(CreateERC1155Transfer.validation), + CreateERC1155Transfer.controller, +); + +router.delete( + '/:id', + guard.check(['erc1155:read', 'erc1155:write']), + assertRequestInput(RemoveERC1155.validation), + RemoveERC1155.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/erc1155/erc1155.test.ts b/apps/api/src/app/controllers/erc1155/erc1155.test.ts new file mode 100644 index 000000000..b222e7714 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/erc1155.test.ts @@ -0,0 +1,89 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { isAddress } from 'web3-utils'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { createImage } from '@thxnetwork/api/util/jest/images'; + +const user = request.agent(app); + +describe('ERC1155', () => { + const chainId = ChainId.Hardhat, + name = 'Planets of the Galaxy', + description = 'Collection full of rarities.'; + let erc1155ID: string; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /erc1155', () => { + it('should create and return contract details', async () => { + const logoImg = createImage(); + await user + .post('/v1/erc1155') + .set('Authorization', dashboardAccessToken) + .attach('file', logoImg, { filename: 'logoImg.jpg', contentType: 'image/jpg' }) + .field({ + chainId, + name, + description, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(body.chainId).toBe(chainId); + expect(body.name).toBe(name); + expect(body.description).toBe(description); + expect(isAddress(body.address)).toBe(true); + expect(body.logoImgUrl).toBeDefined(); + erc1155ID = body._id; + }) + .expect(201); + }); + }); + + describe('GET /erc1155/:id', () => { + it('should return contract details', (done) => { + user.get('/v1/erc1155/' + erc1155ID) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.chainId).toBe(chainId); + expect(body.name).toBe(name); + expect(body.description).toBe(description); + expect(isAddress(body.address)).toBe(true); + expect(body.logoImgUrl).toBeDefined(); + }) + .expect(200, done); + }); + it('should 400 for invalid ID', (done) => { + user.get('/v1/erc1155/' + 'invalid_id') + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.errors[0].msg).toContain('Invalid value'); + }) + .expect(400, done); + }); + it('should 404 if not known', (done) => { + user.get('/v1/erc1155/' + '62397f69760ac5f9ab4454df') + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.error.message).toContain('Not Found'); + }) + .expect(404, done); + }); + describe('PATCH /erc1155/:id', () => { + it('should update a created token', (done) => { + user.patch('/v1/erc1155/' + erc1155ID) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body).toBeDefined(); + }) + .expect(200, done); + }); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc1155/get.controller.ts b/apps/api/src/app/controllers/erc1155/get.controller.ts new file mode 100644 index 000000000..943de3d44 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/get.controller.ts @@ -0,0 +1,21 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + let erc1155 = await ERC1155Service.findById(req.params.id); + + if (!erc1155) throw new NotFoundError(); + // Check if pending transaction is mined. + if (!erc1155.address) erc1155 = await ERC1155Service.queryDeployTransaction(erc1155); + // Still no address. + if (!erc1155.address) return res.send(erc1155); + const owner = await erc1155.contract.methods.owner().call(); + + res.json({ ...erc1155.toJSON(), owner }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/import/erc1155-import.test.ts b/apps/api/src/app/controllers/erc1155/import/erc1155-import.test.ts new file mode 100644 index 000000000..f7ed86915 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/import/erc1155-import.test.ts @@ -0,0 +1,123 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { dashboardAccessToken, sub } from '@thxnetwork/api/util/jest/constants'; +import { ERC1155TokenState } from '@thxnetwork/common/enums'; +import { ERC1155Document } from '@thxnetwork/api/models/ERC1155'; +import { alchemy } from '@thxnetwork/api/util/alchemy'; +import { deployERC1155, mockGetNftsForOwner } from '@thxnetwork/api/util/jest/erc1155'; +import { PoolDocument } from '@thxnetwork/api/models'; +import { Contract } from 'web3-eth-contract'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { ethers } from 'ethers'; +import TransactionService from '@thxnetwork/api/services/TransactionService'; + +const user = request.agent(app); + +describe('ERC1155 import', () => { + let erc1155: ERC1155Document, pool: PoolDocument, nftContract: Contract; + const chainId = ChainId.Hardhat, + nftName = 'Test Collection'; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /pools', () => { + it('HTTP 201', (done) => { + user.post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send({ chainId }) + .expect((res: request.Response) => { + pool = res.body; + }) + .expect(201, done); + }); + }); + + describe('POST /erc1155/import', () => { + it('HTTP 201`', async () => { + // Create 1 NFT collection + nftContract = await deployERC1155(); + const id = 1; + const amount = 1; + // Mint 1 token in the collection + await TransactionService.sendAsync( + nftContract.options.address, + nftContract.methods.mint(pool.safeAddress, id, amount, ethers.constants.HashZero), + chainId, + ); + + // Mock Alchemy SDK return value for getNftsForOwner + jest.spyOn(alchemy.nft, 'getNftsForOwner').mockImplementation(() => + Promise.resolve(mockGetNftsForOwner(nftContract.options.address) as any), + ); + + // Run the import for the deployed contract address + await user + .post('/v1/erc1155/import') + .set({ 'Authorization': dashboardAccessToken, 'X-PoolId': pool._id }) + .send({ chainId, contractAddress: nftContract.options.address, name: nftName }) + .expect(({ body }: request.Response) => { + expect(body.erc1155._id).toBeDefined(); + expect(body.erc1155.address).toBe(nftContract.options.address); + + erc1155 = body.erc1155; + }) + .expect(201); + }); + }); + + describe('GET /erc1155/:id', () => { + const { defaultAccount } = getProvider(chainId); + + it('HTTP 200', (done) => { + user.get(`/v1/erc1155/${erc1155._id}`) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.chainId).toBe(chainId); + expect(body.sub).toBe(sub); + expect(body.name).toBe(nftName); + expect(body.address).toBe(nftContract.options.address); + expect(body.owner).toBe(defaultAccount); + }) + .expect(200, done); + }); + }); + + describe('GET /erc1155/token', () => { + it('HTTP 200', (done) => { + user.get(`/v1/erc1155/token`) + .query({ walletId: pool.safe._id }) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.length).toBe(1); + expect(body[0].sub).toBe(sub); + expect(body[0].erc1155Id).toBe(erc1155._id); + expect(body[0].state).toBe(ERC1155TokenState.Minted); + expect(body[0].recipient).toBe(pool.safeAddress); + expect(body[0].tokenUri).toBeDefined(); + expect(body[0].tokenId).toBeDefined(); + expect(body[0].metadataId).toBeDefined(); + }) + .expect(200, done); + }); + }); + + describe('GET /erc1155/:id/metadata', () => { + it('HTTP 200', (done) => { + user.get(`/v1/erc1155/${erc1155._id}/metadata`) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.total).toBe(1); + expect(body.results[0].name).toBeDefined(); + expect(body.results[0].description).toBeDefined(); + expect(body.results[0].image).toBeDefined(); + }) + .expect(200, done); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc1155/import/post.controller.ts b/apps/api/src/app/controllers/erc1155/import/post.controller.ts new file mode 100644 index 000000000..05c19128b --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/import/post.controller.ts @@ -0,0 +1,104 @@ +import { body } from 'express-validator'; +import { Request, Response } from 'express'; +import { ERC1155Token } from '@thxnetwork/api/models/ERC1155Token'; +import { ERC1155 } from '@thxnetwork/api/models/ERC1155'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC1155TokenState } from '@thxnetwork/common/enums'; +import { getNFTsForOwner, parseIPFSImageUrl } from '@thxnetwork/api/util/alchemy'; +import { ChainId, NFTVariant } from '@thxnetwork/common/enums'; +import { logger } from '@thxnetwork/api/util/logger'; +import { ERC1155Metadata } from '@thxnetwork/api/models/ERC1155Metadata'; +import { toChecksumAddress } from 'web3-utils'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [body('contractAddress').exists().isString(), body('chainId').exists().isNumeric()]; + +const controller = async (req: Request, res: Response) => { + const chainId = Number(req.body.chainId) as ChainId; + const contractAddress = toChecksumAddress(req.body.contractAddress); + const pool = await PoolService.getById(req.header('X-PoolId')); + const ownedNfts = await getNFTsForOwner(pool.safeAddress, contractAddress); + if (!ownedNfts.length) throw new NotFoundError('Could not find NFT tokens for this contract address'); + + let erc1155 = await ERC1155.findOne({ + sub: req.auth.sub, + chainId, + address: contractAddress, + }); + + // If erc1155 already exists check if it is owned by the authenticated user + if (erc1155 && erc1155.sub !== req.auth.sub) { + throw new ForbiddenError('This is not your contract.'); + } + + // If erc1155 is owned or not existing continue with update or upsert + erc1155 = await ERC1155.findOneAndUpdate( + { + chainId, + sub: req.auth.sub, + address: contractAddress, + }, + { + chainId, + sub: req.auth.sub, + address: contractAddress, + variant: NFTVariant.ERC1155, + name: req.body.name, + archived: false, + baseURL: '', + }, + { upsert: true, new: true }, + ); + const erc1155Tokens = await Promise.all( + ownedNfts.map(async ({ name, description, image, collection, tokenId, tokenUri }) => { + try { + const erc1155Id = erc1155.id; + const imageUrl = parseIPFSImageUrl(image.originalUrl); + const metadata = await ERC1155Metadata.findOneAndUpdate( + { + erc1155Id, + tokenId, + }, + { + erc1155Id, + tokenId, + name, + description, + imageUrl, + image: imageUrl, + externalUrl: collection.externalUrl, + }, + { upsert: true, new: true }, + ); + const walletId = String(pool.safe._id); + const erc1155Token = await ERC1155Token.findOneAndUpdate( + { + erc1155Id, + tokenId, + sub: req.auth.sub, + recipient: pool.safeAddress, + }, + { + erc1155Id, + tokenId, + walletId, + tokenUri, + sub: req.auth.sub, + recipient: pool.safeAddress, + state: ERC1155TokenState.Minted, + metadataId: String(metadata._id), + }, + { upsert: true, new: true }, + ); + + return { ...erc1155Token.toJSON(), metadata: metadata.toJSON() }; + } catch (error) { + logger.error(error); + } + }), + ); + + res.status(201).json({ erc1155, erc1155Tokens }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/import/preview/post.controller.ts b/apps/api/src/app/controllers/erc1155/import/preview/post.controller.ts new file mode 100644 index 000000000..d365f80e3 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/import/preview/post.controller.ts @@ -0,0 +1,22 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { getNFTsForOwner, parseIPFSImageUrl } from '@thxnetwork/api/util/alchemy'; + +const validation = [body('address').exists().isString(), body('contractAddress').exists().isString()]; + +const controller = async (req: Request, res: Response) => { + const ownedNFTs = await getNFTsForOwner(req.body.address, req.body.contractAddress); + res.status(200).json( + ownedNFTs.map((nft) => { + return { + balance: nft.balance, + name: nft.name, + description: nft.description, + tokenId: nft.tokenId, + tokenUri: nft.tokenUri, + image: parseIPFSImageUrl(nft.image.originalUrl), + }; + }), + ); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/list.controller.ts b/apps/api/src/app/controllers/erc1155/list.controller.ts new file mode 100644 index 000000000..a26f607c8 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/list.controller.ts @@ -0,0 +1,13 @@ +import { Request, Response } from 'express'; +import { ERC1155Document } from '@thxnetwork/api/models/ERC1155'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const result = await ERC1155Service.findBySub(req.auth.sub); + + res.json(result.map((erc1155: ERC1155Document) => erc1155._id)); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/metadata/delete.controller.ts b/apps/api/src/app/controllers/erc1155/metadata/delete.controller.ts new file mode 100644 index 000000000..a6fc60555 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/metadata/delete.controller.ts @@ -0,0 +1,13 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; + +const validation = [param('metadataId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC1155 Metadata'] + await ERC1155Service.deleteMetadata(req.params.metadataId); + res.status(200).json({ success: true }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/metadata/get.controller.ts b/apps/api/src/app/controllers/erc1155/metadata/get.controller.ts new file mode 100644 index 000000000..323cf385c --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/metadata/get.controller.ts @@ -0,0 +1,13 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; + +const validation = [param('id').isMongoId(), param('metadataId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC1155 Metadata'] + const metadata = await ERC1155Service.findMetadataById(req.params.metadataId); + res.json(metadata); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/metadata/list.controller.ts b/apps/api/src/app/controllers/erc1155/metadata/list.controller.ts new file mode 100644 index 000000000..8735e715c --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/metadata/list.controller.ts @@ -0,0 +1,25 @@ +import { Request, Response } from 'express'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import { param, query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [ + param('id').isMongoId(), + query('limit').optional().isInt({ gt: 0 }), + query('page').optional().isInt({ gt: 0 }), +]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC1155'] + const erc1155 = await ERC1155Service.findById(req.params.id); + if (!erc1155) throw new NotFoundError('Could not find this NFT in the database'); + + const result = await ERC1155Service.findMetadataByNFT( + req.params.id, + req.query.page ? Number(req.query.page) : null, + req.query.limit ? Number(req.query.limit) : null, + ); + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/metadata/patch.controller.ts b/apps/api/src/app/controllers/erc1155/metadata/patch.controller.ts new file mode 100644 index 000000000..b4367c831 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/metadata/patch.controller.ts @@ -0,0 +1,34 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; + +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import { BadRequestError, NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [ + param('id').isMongoId(), + param('metadataId').isMongoId(), + body('title').optional().isString().isLength({ min: 0, max: 100 }), + body('description').optional().isString().isLength({ min: 0, max: 400 }), + body('attributes').exists(), +]; + +const controller = async (req: Request, res: Response) => { + const erc1155 = await ERC1155Service.findById(req.params.id); + if (!erc1155) throw new NotFoundError('Could not find this NFT in the database'); + + const metadata = await ERC1155Service.findMetadataById(req.params.metadataId); + if (!metadata) throw new NotFoundError('Could not find this NFT Metadata in the database'); + + const tokens = metadata.tokens || []; + if (tokens.length) throw new BadRequestError('There token minted with this metadata'); + + await metadata.updateOne({ + title: req.body.title, + description: req.body.description, + attributes: req.body.attributes, + }); + + res.json({ ...metadata.toJSON(), tokens }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/metadata/post.controller.ts b/apps/api/src/app/controllers/erc1155/metadata/post.controller.ts new file mode 100644 index 000000000..70b1658a4 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/metadata/post.controller.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC1155Metadata } from '@thxnetwork/api/models/ERC1155Metadata'; +import { IPFS_BASE_URL, NODE_ENV } from '@thxnetwork/api/config/secrets'; +import { ERC1155Token } from '@thxnetwork/api/models/ERC1155Token'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import IPFSService from '@thxnetwork/api/services/IPFSService'; + +const validation = [ + param('id').isMongoId(), + body('name').optional().isString(), + body('imageUrl').optional().isURL(), + body('description').optional().isString(), + body('externalUrl').optional().isURL(), +]; + +const controller = async (req: Request, res: Response) => { + const erc1155 = await ERC1155Service.findById(req.params.id); + if (!erc1155) throw new NotFoundError('Could not find this NFT in the database'); + + let image = req.body.imageUrl; + + if (req.body.imageUrl && NODE_ENV === 'production') { + const cid = await IPFSService.addUrlSource(req.body.imageUrl); + image = IPFS_BASE_URL + cid; + } + + const erc1155Id = String(erc1155._id); + const count = await ERC1155Metadata.countDocuments({ erc1155Id }); + const tokenId = count + 1; + const metadata = await ERC1155Metadata.create({ + erc1155Id, + name: req.body.name, + image, + imageUrl: req.body.imageUrl, + description: req.body.description, + externalUrl: req.body.externalUrl, + tokenId, + }); + + // Should also create token + await ERC1155Token.create({ + sub: req.auth.sub, + erc1155Id, + metadatId: metadata._id, + tokenId, + }); + + res.status(201).json(metadata); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/patch.controller.ts b/apps/api/src/app/controllers/erc1155/patch.controller.ts new file mode 100644 index 000000000..88d871ec3 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/patch.controller.ts @@ -0,0 +1,17 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC1155'] + const erc1155 = await ERC1155Service.findById(req.params.id); + if (!erc1155) throw new NotFoundError('Could not find the token for this id'); + if (erc1155.sub !== req.auth.sub) throw new ForbiddenError('Not your ERC721'); + + const result = await ERC1155Service.update(erc1155, req.body); + return res.json(result); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/post.controller.ts b/apps/api/src/app/controllers/erc1155/post.controller.ts new file mode 100644 index 000000000..2224d6c6c --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/post.controller.ts @@ -0,0 +1,37 @@ +import { Request, Response } from 'express'; +import { body, check, query } from 'express-validator'; +import { NFTVariant } from '@thxnetwork/common/enums'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import ImageService from '@thxnetwork/api/services/ImageService'; + +const validation = [ + body('name').exists().isString(), + body('description').exists().isString(), + body('chainId').exists().isNumeric(), + check('file') + .optional() + .custom((value, { req }) => { + return ['jpg', 'jpeg', 'gif', 'png'].includes(req.file.mimetype); + }), + query('forceSync').optional().isBoolean(), +]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC1155'] + const logoImgUrl = req.file && (await ImageService.upload(req.file)); + const forceSync = req.query.forceSync !== undefined ? req.query.forceSync === 'true' : false; + const erc1155 = await ERC1155Service.deploy( + { + variant: NFTVariant.ERC1155, + sub: req.auth.sub, + chainId: req.body.chainId, + name: req.body.name, + description: req.body.description, + logoImgUrl, + }, + forceSync, + ); + res.status(201).json(erc1155); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/token/get.controller.ts b/apps/api/src/app/controllers/erc1155/token/get.controller.ts new file mode 100644 index 000000000..b8d781b09 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/token/get.controller.ts @@ -0,0 +1,34 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [param('id').isMongoId(), param('walletId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const token = await ERC1155Service.queryMintTransaction(await ERC1155Service.findTokenById(req.params.id)); + if (!token) throw new NotFoundError('ERC1155Token not found'); + + const erc1155 = await ERC1155Service.findById(token.erc1155Id); + if (!erc1155) throw new NotFoundError('ERC1155 not found'); + + const metadata = await ERC1155Service.findMetadataById(token.metadataId); + if (!metadata) throw new NotFoundError('ERC1155Metadata not found'); + + const wallet = await SafeService.findById(req.query.walletId as string); + if (!wallet) throw new NotFoundError('Wallet not found for account'); + + const balance = await erc1155.contract.methods.balanceOf(wallet.address, metadata.tokenId).call(); + const tokenUri = token.tokenId ? await erc1155.contract.methods.uri(token.tokenId).call() : ''; + + res.json({ + ...token.toJSON(), + nft: erc1155.toJSON(), + metadata: metadata.toJSON(), + tokenUri, + balance, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/token/list.controller.ts b/apps/api/src/app/controllers/erc1155/token/list.controller.ts new file mode 100644 index 000000000..9541e7cb4 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/token/list.controller.ts @@ -0,0 +1,30 @@ +import { Request, Response } from 'express'; +import { ERC1155TokenDocument } from '@thxnetwork/api/models/ERC1155Token'; +import { query } from 'express-validator'; +import { BadRequestError } from '@thxnetwork/api/util/errors'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; + +const validation = [query('walletId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const wallet = await SafeService.findById(req.query.walletId as string); + if (!wallet) throw new BadRequestError('Wallet not found'); + + const tokens = await ERC1155Service.findTokensByWallet(wallet); + const result = await Promise.all( + tokens.map(async (token: ERC1155TokenDocument) => { + const erc1155 = await ERC1155Service.findById(token.erc1155Id); + if (!erc1155) return; + + const metadata = await ERC1155Service.findMetadataById(token.metadataId); + if (!metadata) return; + + return Object.assign(token.toJSON() as TERC1155Token, { metadata, nft: erc1155 }); + }), + ); + + res.json(result.reverse().filter((token) => !!token)); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc1155/transfer/post.controller.ts b/apps/api/src/app/controllers/erc1155/transfer/post.controller.ts new file mode 100644 index 000000000..3ce087089 --- /dev/null +++ b/apps/api/src/app/controllers/erc1155/transfer/post.controller.ts @@ -0,0 +1,42 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC1155Token } from '@thxnetwork/api/models/ERC1155Token'; +import { Transaction } from '@thxnetwork/api/models/Transaction'; +import { ERC1155 } from '@thxnetwork/api/models/ERC1155'; +import ERC1155Service from '@thxnetwork/api/services/ERC1155Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [ + body('walletId').isMongoId(), + body('erc1155Id').isMongoId(), + body('erc1155TokenId').isMongoId(), + body('erc1155Amount').isInt(), + body('to').isString(), +]; + +const controller = async (req: Request, res: Response) => { + const erc1155 = await ERC1155.findById(req.body.erc1155Id); + if (!erc1155) throw new NotFoundError('Could not find the ERC1155'); + + const erc1155Token = await ERC1155Token.findById(req.body.erc1155TokenId); + if (!erc1155Token) throw new NotFoundError('Could not find token for wallet'); + + const wallet = await SafeService.findById(req.body.walletId); + if (!wallet) throw new NotFoundError('Could not find wallet for account'); + + const balance = await erc1155.contract.methods.balanceOf(wallet.address, erc1155Token.tokenId).call(); + if (Number(balance) < Number(req.body.erc1155Amount)) throw new ForbiddenError('Insufficient balance'); + + const receiverToken = await ERC1155Service.transferFrom( + erc1155, + wallet, + req.body.to, + erc1155Token, + req.body.erc1155Amount, + ); + const tx = await Transaction.findById(receiverToken.transactions[0]); + + res.status(201).json(tx); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/allowance/allowance.router.ts b/apps/api/src/app/controllers/erc20/allowance/allowance.router.ts new file mode 100644 index 000000000..0c07c5e26 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/allowance/allowance.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import * as ListController from './get.controller'; +import * as CreateController from './post.controller'; + +const router: express.Router = express.Router(); + +router.post( + '/', + guard.check(['erc20:read']), + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.get('/', guard.check(['erc20:read']), assertRequestInput(ListController.validation), ListController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/erc20/allowance/get.controller.ts b/apps/api/src/app/controllers/erc20/allowance/get.controller.ts new file mode 100644 index 000000000..0a2df2a60 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/allowance/get.controller.ts @@ -0,0 +1,27 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import ContractService from '@thxnetwork/api/services/ContractService'; +import WalletService from '@thxnetwork/api/services/WalletService'; + +const validation = [ + query('tokenAddress').isEthereumAddress(), + query('spender').isEthereumAddress(), + query('walletId').isMongoId(), +]; + +const controller = async (req: Request, res: Response) => { + const walletId = req.query.walletId as string; + const wallet = await WalletService.findById(walletId); + if (!wallet) throw new NotFoundError('Could not find wallet for account'); + + const contract = ContractService.getContract( + 'THXERC20_LimitedSupply', + wallet.chainId, + req.query.tokenAddress as string, + ); + const allowance = await contract.allowance(wallet.address, req.query.spender); + + res.json({ allowanceInWei: allowance.toString() }); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/allowance/post.controller.ts b/apps/api/src/app/controllers/erc20/allowance/post.controller.ts new file mode 100644 index 000000000..982c380af --- /dev/null +++ b/apps/api/src/app/controllers/erc20/allowance/post.controller.ts @@ -0,0 +1,40 @@ +import { Request, Response } from 'express'; +import { body, query } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { BigNumber } from 'alchemy-sdk'; +import { getArtifact } from '@thxnetwork/api/hardhat'; +import TransactionService from '@thxnetwork/api/services/TransactionService'; +import WalletService from '@thxnetwork/api/services/WalletService'; + +const validation = [ + body('tokenAddress').isEthereumAddress(), + body('spender').isEthereumAddress(), + body('amountInWei').isString(), + query('walletId').isMongoId(), +]; + +const controller = async (req: Request, res: Response) => { + const walletId = req.query.walletId as string; + const wallet = await WalletService.findById(walletId); + if (!wallet) throw new NotFoundError('Wallet not found'); + if (wallet.sub !== req.auth.sub) throw new ForbiddenError('Wallet not owned by sub.'); + + const { web3 } = getProvider(wallet.chainId); + const { abi } = getArtifact('THXERC20_LimitedSupply'); + const contract = new web3.eth.Contract(abi, req.body.tokenAddress); + const amount = await contract.methods.balanceOf(wallet.address).call(); + + // Check sufficient BPT Balance + if (BigNumber.from(amount).lt(BigNumber.from(req.body.amountInWei))) { + throw new ForbiddenError('Insufficient balance'); + } + + const fn = contract.methods.approve(req.body.spender, req.body.amountInWei); + + // Propose tx data to relayer and return safeTxHash to client to sign + const tx = await TransactionService.sendSafeAsync(wallet, contract.options.address, fn); + + res.status(201).json([tx]); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/balance/balance.router.ts b/apps/api/src/app/controllers/erc20/balance/balance.router.ts new file mode 100644 index 000000000..3b176152d --- /dev/null +++ b/apps/api/src/app/controllers/erc20/balance/balance.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import * as ReadController from './get.controller'; + +const router: express.Router = express.Router(); + +router.get('/', guard.check(['erc20:read']), assertRequestInput(ReadController.validation), ReadController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/erc20/balance/get.controller.ts b/apps/api/src/app/controllers/erc20/balance/get.controller.ts new file mode 100644 index 000000000..31e944cfe --- /dev/null +++ b/apps/api/src/app/controllers/erc20/balance/get.controller.ts @@ -0,0 +1,23 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import ContractService from '@thxnetwork/api/services/ContractService'; +import WalletService from '@thxnetwork/api/services/WalletService'; + +const validation = [query('walletId').isMongoId(), query('tokenAddress').isEthereumAddress()]; + +const controller = async (req: Request, res: Response) => { + const walletId = req.query.walletId as string; + const wallet = await WalletService.findById(walletId); + if (!wallet) throw new NotFoundError('Wallet not found'); + + const contract = ContractService.getContract( + 'THXERC20_LimitedSupply', + wallet.chainId, + req.query.tokenAddress as string, + ); + const balance = await contract.balanceOf(wallet.address); + + res.json({ balanceInWei: balance.toString() }); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/delete.controller.ts b/apps/api/src/app/controllers/erc20/delete.controller.ts new file mode 100644 index 000000000..79e2529df --- /dev/null +++ b/apps/api/src/app/controllers/erc20/delete.controller.ts @@ -0,0 +1,12 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { ERC20 } from '@thxnetwork/api/models'; + +const validation = [param('id').exists().isMongoId()]; + +const controller = async (req: Request, res: Response) => { + await ERC20.deleteOne({ _id: req.params.id }); + return res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/erc20.router.ts b/apps/api/src/app/controllers/erc20/erc20.router.ts new file mode 100644 index 000000000..4b5839b72 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/erc20.router.ts @@ -0,0 +1,72 @@ +import express from 'express'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import { upload } from '@thxnetwork/api/util/multer'; + +import RouterAllowance from './allowance/allowance.router'; +import RouterTransfer from './transfer/transfer.router'; +import RouterBalance from './balance/balance.router'; +import RouterPreview from './preview/preview.router'; + +import * as CreateController from './post.controller'; +import * as ReadController from './get.controller'; +import * as UpdateController from './patch.controller'; +import * as DeleteController from './delete.controller'; +import * as ListController from './list.controller'; + +import * as ListERC20Token from './token/list.controller'; +import * as ReadERC20Token from './token/get.controller'; +import * as ImportERC20 from './token/post.controller'; + +const router: express.Router = express.Router(); + +router.use('/transfer', RouterTransfer); +router.use('/balance', RouterBalance); +router.use('/allowance', RouterAllowance); +router.use('/preview', RouterPreview); + +// Token Resource should move into /wallet +router.get( + '/token', + guard.check(['erc20:read']), + assertRequestInput(ListERC20Token.validation), + ListERC20Token.controller, +); +router.get('/token/:id', guard.check(['erc20:read']), ReadERC20Token.controller); + +// Should be /import controller +router.post( + '/token', + guard.check(['erc20:write', 'erc20:read']), + assertRequestInput(ImportERC20.validation), + ImportERC20.controller, +); +// End + +router.post( + '/', + upload.single('file'), + guard.check(['erc20:write', 'erc20:read']), + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.get( + '/:id', + guard.check(['erc20:read']), + assertRequestInput(ReadController.validation), + ReadController.controller, +); +router.patch( + '/:id', + guard.check(['erc20:write', 'erc20:read']), + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/:id', + guard.check(['erc20:write']), + assertRequestInput(DeleteController.validation), + DeleteController.controller, +); +router.get('/', guard.check(['erc20:read']), assertRequestInput(ListController.validation), ListController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/erc20/erc20.test.ts b/apps/api/src/app/controllers/erc20/erc20.test.ts new file mode 100644 index 000000000..79c1aac86 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/erc20.test.ts @@ -0,0 +1,129 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId, ERC20Type } from '@thxnetwork/common/enums'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { createImage } from '@thxnetwork/api/util/jest/images'; +import { toWei } from 'web3-utils'; +import { isAddress } from 'ethers/lib/utils'; + +const http = request.agent(app); + +describe('ERC20', () => { + const totalSupply = toWei('1000'), + name = 'Test Token', + symbol = 'TTK'; + let tokenAddress: string, tokenName: string, tokenSymbol: string, erc20Id: string; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /erc20', () => { + it('Able to create unlimited token and return address', (done) => { + http.post('/v1/erc20') + .set('Authorization', dashboardAccessToken) + .send({ + name: 'Test Token', + symbol: 'TTK', + chainId: ChainId.Hardhat, + totalSupply: 0, + type: ERC20Type.Unlimited, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(isAddress(body.address)).toBe(true); + }) + .expect(201, done); + }); + + it('Able to create limited token and return address', async () => { + const image = createImage(); + await http + .post('/v1/erc20') + .set('Authorization', dashboardAccessToken) + .attach('file', image, { + filename: 'test.jpg', + contentType: 'image/jpg', + }) + .field({ + name, + symbol, + chainId: ChainId.Hardhat, + totalSupply, + type: ERC20Type.Limited, + }) + .expect(({ body }: request.Response) => { + expect(isAddress(body._id)).toBeDefined(); + expect(isAddress(body.address)).toBe(true); + expect(body.logoImgUrl).toBeDefined(); + erc20Id = body._id; + tokenAddress = body.address; + tokenName = body.name; + tokenSymbol = body.symbol; + }) + .expect(201); + }); + + it('Able to return list of created token', (done) => { + http.get('/v1/erc20') + .set('Authorization', dashboardAccessToken) + .expect(({ body }: request.Response) => { + expect(body.length).toEqual(2); + }) + .expect(200, done); + }); + + it('Able to return a created token', (done) => { + http.get('/v1/erc20/' + erc20Id) + .set('Authorization', dashboardAccessToken) + .expect(({ body }: request.Response) => { + expect(body).toBeDefined(); + expect(isAddress(body.address)).toBe(true); + expect(body.type).toBe(ERC20Type.Limited); + expect(body.totalSupplyInWei).toBe(totalSupply); + expect(body.name).toBe(name); + expect(body.symbol).toBe(symbol); + expect(body.decimals).toBe(18); + }) + .expect(200, done); + }); + }); + + describe('PATCH /erc20', () => { + it('should to update a created token', (done) => { + http.patch('/v1/erc20/' + erc20Id) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body).toBeDefined(); + }) + .expect(200, done); + }); + }); + describe('DELETE /erc20/:id', () => { + it('Able to delete created token', (done) => { + http.delete('/v1/erc20/' + erc20Id) + .set('Authorization', dashboardAccessToken) + .expect(204, done); + }); + }); + + describe('POST /erc20/preview', () => { + it('should return name symbol and total supply of an oncChain ERC20Token', (done) => { + http.get('/v1/erc20/preview') + .set('Authorization', dashboardAccessToken) + .query({ + chainId: ChainId.Hardhat, + address: tokenAddress, + }) + .send() + .expect(({ body }: request.Response) => { + expect(body).toBeDefined(); + expect(body.name).toBe(tokenName); + expect(body.symbol).toBe(tokenSymbol); + expect(body.totalSupplyInWei).toBe(totalSupply); + }) + .expect(200, done); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc20/get.controller.ts b/apps/api/src/app/controllers/erc20/get.controller.ts new file mode 100644 index 000000000..0f49f8840 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/get.controller.ts @@ -0,0 +1,37 @@ +import { Request, Response } from 'express'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; +import { param } from 'express-validator'; +import { fromWei } from 'web3-utils'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + let erc20 = await ERC20Service.queryDeployTransaction(await ERC20Service.getById(req.params.id)); + if (!erc20) throw new NotFoundError('ERC20 not found'); + + // Check if pending transaction is mined. + if (!erc20.address) erc20 = await ERC20Service.queryDeployTransaction(erc20); + + // Still no address. + if (!erc20.address) return res.send(erc20); + + const { defaultAccount } = getProvider(erc20.chainId); + const [totalSupplyInWei, decimalsString, adminBalanceInWei] = await Promise.all([ + erc20.contract.methods.totalSupply().call(), + erc20.contract.methods.decimals().call(), + erc20.contract.methods.balanceOf(defaultAccount).call(), + ]); + const decimals = Number(decimalsString); + const adminBalance = Number(fromWei(adminBalanceInWei, 'ether')); + + res.status(200).json({ + ...erc20.toJSON(), + totalSupplyInWei, + decimals, + adminBalance, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/list.controller.ts b/apps/api/src/app/controllers/erc20/list.controller.ts new file mode 100644 index 000000000..40d5ecc8c --- /dev/null +++ b/apps/api/src/app/controllers/erc20/list.controller.ts @@ -0,0 +1,11 @@ +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; +import { Request, Response } from 'express'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const erc20s = await ERC20Service.findBySub(req.auth.sub); + return res.json(erc20s); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/patch.controller.ts b/apps/api/src/app/controllers/erc20/patch.controller.ts new file mode 100644 index 000000000..c1b8b88ae --- /dev/null +++ b/apps/api/src/app/controllers/erc20/patch.controller.ts @@ -0,0 +1,15 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const erc20 = await ERC20Service.getById(req.params.id); + if (!erc20) throw new NotFoundError('Could not find the token for this id'); + + const result = await ERC20Service.update(erc20, req.body); + return res.json(result); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/post.controller.ts b/apps/api/src/app/controllers/erc20/post.controller.ts new file mode 100644 index 000000000..902fe4be3 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/post.controller.ts @@ -0,0 +1,39 @@ +import { Request, Response } from 'express'; +import { body, check, query } from 'express-validator'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; +import ImageService from '@thxnetwork/api/services/ImageService'; + +const validation = [ + body('name').exists().isString(), + body('symbol').exists().isString(), + body('chainId').exists().isNumeric(), + body('type').exists().isNumeric(), + body('totalSupply').optional().isNumeric(), + check('file') + .optional() + .custom((value, { req }) => { + return ['jpg', 'jpeg', 'gif', 'png'].includes(req.file.mimetype); + }), + query('forceSync').optional().isBoolean(), +]; + +const controller = async (req: Request, res: Response) => { + const logoImgUrl = req.file && (await ImageService.upload(req.file)); + const forceSync = req.query.forceSync !== undefined ? req.query.forceSync === 'true' : false; + + const erc20 = await ERC20Service.deploy( + { + name: req.body.name, + symbol: req.body.symbol, + chainId: req.body.chainId, + totalSupply: req.body.totalSupply, + type: req.body.type, + sub: req.auth.sub, + logoImgUrl, + }, + forceSync, + ); + + res.status(201).json(erc20); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/preview/get.controller.ts b/apps/api/src/app/controllers/erc20/preview/get.controller.ts new file mode 100644 index 000000000..efc11edb9 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/preview/get.controller.ts @@ -0,0 +1,20 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { ChainId } from '@thxnetwork/common/enums'; +import ContractService from '@thxnetwork/api/services/ContractService'; + +const validation = [query('chainId').isInt(), query('address').isEthereumAddress()]; + +const controller = async (req: Request, res: Response) => { + const chainId = req.query.chainId as unknown as ChainId; + const contractAddress = req.query.address as string; + const contract = ContractService.getContract('THXERC20_LimitedSupply', chainId, contractAddress); + const [name, symbol, totalSupplyInWei] = await Promise.all([ + contract.name(), + contract.symbol(), + contract.totalSupply(), + ]); + + res.json({ name, symbol, totalSupplyInWei: totalSupplyInWei.toString() }); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/preview/preview.router.ts b/apps/api/src/app/controllers/erc20/preview/preview.router.ts new file mode 100644 index 000000000..3b176152d --- /dev/null +++ b/apps/api/src/app/controllers/erc20/preview/preview.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import * as ReadController from './get.controller'; + +const router: express.Router = express.Router(); + +router.get('/', guard.check(['erc20:read']), assertRequestInput(ReadController.validation), ReadController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/erc20/token/get.controller.ts b/apps/api/src/app/controllers/erc20/token/get.controller.ts new file mode 100644 index 000000000..a34aa3da7 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/token/get.controller.ts @@ -0,0 +1,31 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import { fromWei } from 'web3-utils'; +import { BadRequestError, NotFoundError } from '@thxnetwork/api/util/errors'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; +import WalletService from '@thxnetwork/api/services/WalletService'; + +const validation = [param('id').isMongoId(), query('walletId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const token = await ERC20Service.getTokenById(req.params.id); + if (!token) throw new NotFoundError('ERC20Token not found'); + + const erc20 = await ERC20Service.getById(token.erc20Id); + if (!erc20) throw new NotFoundError('ERC20 not found'); + + const wallet = await WalletService.findById(req.query.walletId as string); + if (!wallet) throw new BadRequestError('Wallet not found'); + + const walletBalanceInWei = await erc20.contract.methods.balanceOf(wallet.address).call(); + const walletBalance = Number(fromWei(walletBalanceInWei, 'ether')); + + res.json({ + ...token.toJSON(), + walletBalanceInWei, + walletBalance, + erc20, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/token/list.controller.ts b/apps/api/src/app/controllers/erc20/token/list.controller.ts new file mode 100644 index 000000000..b9997026d --- /dev/null +++ b/apps/api/src/app/controllers/erc20/token/list.controller.ts @@ -0,0 +1,22 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { BadRequestError } from '@thxnetwork/api/util/errors'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [query('walletId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const wallet = await SafeService.findById(req.query.walletId as string); + if (!wallet) throw new BadRequestError('Wallet not found'); + + const tokens = await ERC20Service.getTokensForWallet(wallet); + + res.json( + tokens.reverse().filter((token: TERC20Token & { erc20: TERC20 }) => { + return token && wallet.chainId === token.erc20.chainId; + }), + ); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/token/post.controller.ts b/apps/api/src/app/controllers/erc20/token/post.controller.ts new file mode 100644 index 000000000..9142aa86d --- /dev/null +++ b/apps/api/src/app/controllers/erc20/token/post.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; + +const validation = [ + body('address').exists().isString(), + body('chainId').exists().isInt(), + body('logoImgUrl').optional().isString(), +]; + +const controller = async (req: Request, res: Response) => { + const erc20 = await ERC20Service.importToken( + Number(req.body.chainId), + req.body.address, + req.auth.sub, + req.body.logoImgUrl, + ); + + res.status(201).json(erc20); +}; +export { controller, validation }; diff --git a/apps/campaign/.gitkeep b/apps/api/src/app/controllers/erc20/token/token.router.ts similarity index 100% rename from apps/campaign/.gitkeep rename to apps/api/src/app/controllers/erc20/token/token.router.ts diff --git a/apps/api/src/app/controllers/erc20/transfer/erc20-transfer.test.ts b/apps/api/src/app/controllers/erc20/transfer/erc20-transfer.test.ts new file mode 100644 index 000000000..48fd89d29 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/transfer/erc20-transfer.test.ts @@ -0,0 +1,104 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { WalletDocument, ERC20, ERC20Document } from '@thxnetwork/api/models'; +import { ChainId, ERC20Type, WalletVariant } from '@thxnetwork/common/enums'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { + dashboardAccessToken, + userWalletAddress2, + userWalletPrivateKey, + widgetAccessToken, +} from '@thxnetwork/api/util/jest/constants'; +import { toWei } from 'web3-utils'; +import { poll } from '@thxnetwork/api/util/polling'; +import { signTxHash } from '@thxnetwork/api/util/jest/network'; + +const user = request.agent(app); + +describe('ERC20 Transfer', () => { + let erc20: ERC20Document, wallet: WalletDocument; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /erc20', () => { + it('HTTP 201', (done) => { + user.post('/v1/erc20') + .set('Authorization', dashboardAccessToken) + .send({ + name: 'Test Token', + symbol: 'TTK', + totalSupply: toWei('100', 'ether'), + type: ERC20Type.Limited, + chainId: ChainId.Hardhat, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + erc20 = body; + }) + .expect(201, done); + }); + }); + + describe('GET /wallet and transfer erc20', () => { + it('HTTP 200', (done) => { + user.get('/v1/account/wallets') + .set({ Authorization: widgetAccessToken }) + .send() + .expect(async ({ body }: request.Response) => { + wallet = body.find((w: WalletDocument) => w.variant === WalletVariant.Safe); + expect(wallet).toBeDefined(); + expect(wallet.address).toBeDefined(); + }) + .expect(200, done); + }); + + it('Transfer ERC20', async () => { + const { contract } = await ERC20.findById(erc20._id); + await contract.methods.transfer(wallet.address, toWei('100', 'ether')).send(); + + const balanceInWei = await contract.methods.balanceOf(wallet.address).call(); + expect(balanceInWei).toBe(toWei('100', 'ether')); + }); + }); + + describe('POST /erc20/transfer', () => { + it('HTTP 201', async () => { + const res = await user + .post('/v1/erc20/transfer') + .set({ Authorization: widgetAccessToken }) + .send({ + walletId: String(wallet._id), + erc20Id: erc20._id, + to: userWalletAddress2, + amount: toWei('1', 'ether'), + chainId: ChainId.Hardhat, + }); + expect(res.body.safeTxHash).toBeDefined(); + expect(res.status).toBe(201); + + const { safeTxHash, signature } = await signTxHash( + wallet.address, + res.body.safeTxHash, + userWalletPrivateKey, + ); + const res2 = await user + .post(`/v1/account/wallets/confirm`) + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(wallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash, signature }); + + expect(res2.status).toBe(200); + }); + it('Wait for balance', async () => { + const { contract } = await ERC20.findById(erc20._id); + await poll( + contract.methods.balanceOf(userWalletAddress2).call, + (result: string) => result !== toWei('1', 'ether'), + 1000, + ); + const balanceInWei = await contract.methods.balanceOf(userWalletAddress2).call(); + expect(balanceInWei).toEqual(toWei('1', 'ether')); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc20/transfer/post.controller.ts b/apps/api/src/app/controllers/erc20/transfer/post.controller.ts new file mode 100644 index 000000000..c741ccb32 --- /dev/null +++ b/apps/api/src/app/controllers/erc20/transfer/post.controller.ts @@ -0,0 +1,32 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { InsufficientBalanceError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { BN } from 'bn.js'; +import { ERC20 } from '@thxnetwork/api/models'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import ERC20Service from '@thxnetwork/api/services/ERC20Service'; + +const validation = [ + body('walletId').isMongoId(), + body('erc20Id').isMongoId(), + body('to').isString(), + body('amount').isString(), +]; + +const controller = async (req: Request, res: Response) => { + const erc20 = await ERC20.findById(req.body.erc20Id); + if (!erc20) throw new NotFoundError('Could not find the ERC20'); + + const wallet = await SafeService.findById(req.body.walletId); + if (!wallet) throw new NotFoundError('Could not find wallet for account'); + + const walletBalanceInWei = await erc20.contract.methods.balanceOf(wallet.address).call(); + const balanceInWei = new BN(walletBalanceInWei); + const amountInWei = new BN(req.body.amount); + if (amountInWei.gt(balanceInWei)) throw new InsufficientBalanceError(); + + const tx = await ERC20Service.transferFrom(erc20, wallet, req.body.to, String(amountInWei)); + + res.status(201).json(tx); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc20/transfer/transfer.router.ts b/apps/api/src/app/controllers/erc20/transfer/transfer.router.ts new file mode 100644 index 000000000..36036cb0d --- /dev/null +++ b/apps/api/src/app/controllers/erc20/transfer/transfer.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as CreateController from './post.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post('/', assertRequestInput(CreateController.validation), CreateController.controller); + +export default router; diff --git a/apps/api/src/app/controllers/erc721/delete.controller.ts b/apps/api/src/app/controllers/erc721/delete.controller.ts new file mode 100644 index 000000000..693743e22 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/delete.controller.ts @@ -0,0 +1,16 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { ERC721 } from '@thxnetwork/api/models/ERC721'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const erc721 = await ERC721.findById(req.params.id); + if (erc721.sub !== req.auth.sub) throw new ForbiddenError('Not your ERC721'); + + await erc721.deleteOne(); + + return res.status(204).end(); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/erc721.router.ts b/apps/api/src/app/controllers/erc721/erc721.router.ts new file mode 100644 index 000000000..a1a79c610 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/erc721.router.ts @@ -0,0 +1,107 @@ +import express from 'express'; +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import { upload } from '@thxnetwork/api/util/multer'; + +import * as ReadERC721 from './get.controller'; +import * as ListERC721 from './list.controller'; +import * as ListERC721Metadata from './metadata/list.controller'; +import * as ListERC721Token from './token/list.controller'; +import * as ReadERC721Token from './token/get.controller'; +import * as RemoveERC721 from './delete.controller'; +import * as CreateERC721 from './post.controller'; +import * as CreateMultipleERC721Metadata from './metadata/images/post.controller'; +import * as UpdateERC721 from './patch.controller'; +import * as ReadERC721Metadata from './metadata/get.controller'; +import * as CreateERC721Metadata from './metadata/post.controller'; +import * as PatchERC721Metadata from './metadata/patch.controller'; +import * as DeleteERC721Metadata from './metadata/delete.controller'; +import * as ImportERC721Contract from './import/post.controller'; +import * as PreviewERC721Contract from './import/preview/post.controller'; +import * as CreateERC721Transfer from './transfer/post.controller'; + +const router: express.Router = express.Router(); + +router.get( + '/token', + guard.check(['erc721:read']), + assertRequestInput(ListERC721Token.validation), + ListERC721Token.controller, +); +router.get('/token/:id', guard.check(['erc721:read']), ReadERC721Token.controller); +router.get('/', guard.check(['erc721:read']), assertRequestInput(ListERC721.validation), ListERC721.controller); +router.get('/:id', guard.check(['erc721:read']), assertRequestInput(ReadERC721.validation), ReadERC721.controller); + +router.post( + '/', + upload.single('file'), + guard.check(['erc721:read', 'erc721:write']), + assertRequestInput(CreateERC721.validation), + CreateERC721.controller, +); + +router.post( + '/transfer', + // guard.check(['erc721_transfer:read', 'erc721_transfer:write']), + assertRequestInput(CreateERC721Transfer.validation), + CreateERC721Transfer.controller, +); +router.post( + '/import', + ImportERC721Contract.controller, + assertPoolAccess, + assertRequestInput(ImportERC721Contract.validation), +); +router.post('/preview', assertRequestInput(PreviewERC721Contract.validation), PreviewERC721Contract.controller); +router.patch( + '/:id/metadata/:metadataId', + guard.check(['erc721:write']), + assertRequestInput(PatchERC721Metadata.validation), + PatchERC721Metadata.controller, +); + +router.delete( + '/:id/metadata/:metadataId', + guard.check(['erc721:write']), + assertRequestInput(DeleteERC721Metadata.validation), + DeleteERC721Metadata.controller, +); + +router.get('/:id/metadata', guard.check(['erc721:read']), ListERC721Metadata.controller); + +router.post( + '/:id/metadata/', + guard.check(['erc721:write']), + assertRequestInput(CreateERC721Metadata.validation), + CreateERC721Metadata.controller, +); + +router.post( + '/:id/metadata/zip', + upload.single('file'), + guard.check(['erc721:write']), + assertRequestInput(CreateMultipleERC721Metadata.validation), + CreateMultipleERC721Metadata.controller, +); + +router.patch( + '/:id', + guard.check(['erc721:write', 'erc721:read']), + assertRequestInput(UpdateERC721.validation), + UpdateERC721.controller, +); + +router.get( + '/:id/metadata/:metadataId', + guard.check(['erc721:read']), + ReadERC721Metadata.controller, + assertRequestInput(ReadERC721Metadata.validation), +); + +router.delete( + '/:id', + guard.check(['erc721:read', 'erc721:write']), + assertRequestInput(RemoveERC721.validation), + RemoveERC721.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/erc721/erc721.test.ts b/apps/api/src/app/controllers/erc721/erc721.test.ts new file mode 100644 index 000000000..22f962f5b --- /dev/null +++ b/apps/api/src/app/controllers/erc721/erc721.test.ts @@ -0,0 +1,93 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { isAddress } from 'web3-utils'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { createImage } from '@thxnetwork/api/util/jest/images'; + +const user = request.agent(app); + +describe('ERC721', () => { + const chainId = ChainId.Hardhat, + name = 'Planets of the Galaxy', + symbol = 'GLXY', + description = 'Collection full of rarities.'; + let erc721ID: string; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /erc721', () => { + it('should create and return contract details', async () => { + const logoImg = createImage(); + await user + .post('/v1/erc721') + .set('Authorization', dashboardAccessToken) + .attach('file', logoImg, { filename: 'logoImg.jpg', contentType: 'image/jpg' }) + .field({ + chainId, + name, + symbol, + description, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(body.chainId).toBe(chainId); + expect(body.name).toBe(name); + expect(body.symbol).toBe(symbol); + expect(body.description).toBe(description); + expect(isAddress(body.address)).toBe(true); + expect(body.logoImgUrl).toBeDefined(); + erc721ID = body._id; + }) + .expect(201); + }); + }); + + describe('GET /erc721/:id', () => { + it('should return contract details', (done) => { + user.get('/v1/erc721/' + erc721ID) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.chainId).toBe(chainId); + expect(body.name).toBe(name); + expect(body.symbol).toBe(symbol); + expect(body.description).toBe(description); + expect(isAddress(body.address)).toBe(true); + expect(body.logoImgUrl).toBeDefined(); + }) + .expect(200, done); + }); + it('should 400 for invalid ID', (done) => { + user.get('/v1/erc721/' + 'invalid_id') + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.errors[0].msg).toContain('Invalid value'); + }) + .expect(400, done); + }); + it('should 404 if not known', (done) => { + user.get('/v1/erc721/' + '62397f69760ac5f9ab4454df') + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.error.message).toContain('Not Found'); + }) + .expect(404, done); + }); + describe('PATCH /erc721/:id', () => { + it('should update a created token', (done) => { + user.patch('/v1/erc721/' + erc721ID) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body).toBeDefined(); + }) + .expect(200, done); + }); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc721/get.controller.ts b/apps/api/src/app/controllers/erc721/get.controller.ts new file mode 100644 index 000000000..f4451041d --- /dev/null +++ b/apps/api/src/app/controllers/erc721/get.controller.ts @@ -0,0 +1,28 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + let erc721 = await ERC721Service.findById(req.params.id); + if (!erc721) throw new NotFoundError(); + + // Check if pending transaction is mined. + if (!erc721.address) { + erc721 = await ERC721Service.queryDeployTransaction(erc721); + } + + // Still no address. + if (!erc721.address) { + return res.send(erc721); + } + + const totalSupply = await erc721.contract.methods.totalSupply().call(); + const owner = await erc721.contract.methods.owner().call(); + + res.json({ ...erc721.toJSON(), totalSupply, owner }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/import/erc721-import.test.ts b/apps/api/src/app/controllers/erc721/import/erc721-import.test.ts new file mode 100644 index 000000000..e146ef49d --- /dev/null +++ b/apps/api/src/app/controllers/erc721/import/erc721-import.test.ts @@ -0,0 +1,102 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { Contract } from 'web3-eth-contract'; +import { ChainId } from '@thxnetwork/common/enums'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { dashboardAccessToken, sub } from '@thxnetwork/api/util/jest/constants'; +import { alchemy } from '@thxnetwork/api/util/alchemy'; +import { deployERC721, mockGetNftsForOwner } from '@thxnetwork/api/util/jest/erc721'; +import { ERC721Document, PoolDocument } from '@thxnetwork/api/models'; +import { getProvider } from '@thxnetwork/api/util/network'; +import TransactionService from '@thxnetwork/api/services/TransactionService'; + +const user = request.agent(app); + +describe('ERC721 import', () => { + let erc721: ERC721Document, pool: PoolDocument, nftContract: Contract; + const chainId = ChainId.Hardhat, + nftName = 'Test Collection', + nftSymbol = 'TST'; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /pools', () => { + it('HTTP 201', (done) => { + user.post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send({ chainId }) + .expect((res: request.Response) => { + pool = res.body; + }) + .expect(201, done); + }); + }); + + describe('POST /erc721/import', () => { + it('HTTP 201`', async () => { + // Create 1 NFT collection + nftContract = await deployERC721(nftName, nftSymbol); + + // Mint 1 token in the collection + await TransactionService.sendAsync( + nftContract.options.address, + nftContract.methods.mint(pool.safeAddress, 'tokenuri.json'), + chainId, + ); + + // Mock Alchemy SDK return value for getNftsForOwner + jest.spyOn(alchemy.nft, 'getNftsForOwner').mockImplementation(() => + Promise.resolve(mockGetNftsForOwner(nftContract.options.address, nftName, nftSymbol) as any), + ); + + // Run the import for the deployed contract address + await user + .post('/v1/erc721/import') + .set({ 'Authorization': dashboardAccessToken, 'X-PoolId': pool._id }) + .send({ chainId, contractAddress: nftContract.options.address }) + .expect(({ body }: request.Response) => { + expect(body.erc721._id).toBeDefined(); + expect(body.erc721.address).toBe(nftContract.options.address); + erc721 = body.erc721; + }) + .expect(201); + }); + }); + + describe('GET /erc721/:id', () => { + const { defaultAccount } = getProvider(chainId); + + it('HTTP 200', (done) => { + user.get(`/v1/erc721/${erc721._id}`) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.chainId).toBe(chainId); + expect(body.sub).toBe(sub); + expect(body.name).toBe(nftName); + expect(body.symbol).toBe(nftSymbol); + expect(body.address).toBe(nftContract.options.address); + expect(body.totalSupply).toBe('1'); + expect(body.owner).toBe(defaultAccount); + }) + .expect(200, done); + }); + }); + + describe('GET /erc721/:id/metadata', () => { + it('HTTP 200', (done) => { + user.get(`/v1/erc721/${erc721._id}/metadata`) + .set('Authorization', dashboardAccessToken) + .send() + .expect(({ body }: request.Response) => { + expect(body.total).toBe(1); + expect(body.results[0].name).toBeDefined(); + expect(body.results[0].description).toBeDefined(); + expect(body.results[0].image).toBeDefined(); + expect(body.results[0].externalUrl).toBeDefined(); + }) + .expect(200, done); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc721/import/post.controller.ts b/apps/api/src/app/controllers/erc721/import/post.controller.ts new file mode 100644 index 000000000..6c26c93ea --- /dev/null +++ b/apps/api/src/app/controllers/erc721/import/post.controller.ts @@ -0,0 +1,88 @@ +import { body } from 'express-validator'; +import { Request, Response } from 'express'; +import { ERC721, ERC721Token, ERC721Metadata, Wallet } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { getNFTsForOwner, parseIPFSImageUrl } from '@thxnetwork/api/util/alchemy'; +import { ChainId, ERC721TokenState, NFTVariant } from '@thxnetwork/common/enums'; +import { toChecksumAddress } from 'web3-utils'; + +const validation = [ + body('address').isEthereumAddress(), + body('contractAddress').exists(), + body('chainId').exists().isNumeric(), +]; + +const controller = async (req: Request, res: Response) => { + const chainId = Number(req.body.chainId) as ChainId; + const contractAddress = toChecksumAddress(req.body.contractAddress); + const safeAddress = toChecksumAddress(req.body.address); + const ownedNfts = await getNFTsForOwner(safeAddress, contractAddress); + if (!ownedNfts.length) throw new NotFoundError('Could not find NFT tokens for this contract address'); + + const { address, name, symbol } = ownedNfts[0].contract; + const erc721 = await ERC721.findOneAndUpdate( + { + sub: req.auth.sub, + chainId, + address: toChecksumAddress(address), + }, + { + variant: NFTVariant.ERC721, + sub: req.auth.sub, + chainId, + address: toChecksumAddress(address), + name, + symbol, + archived: false, + }, + { upsert: true, new: true }, + ); + const erc721Tokens = await Promise.all( + ownedNfts.map(async ({ name, description, collection, tokenId, tokenUri, image }) => { + try { + const erc721Id = erc721.id; + const imageUrl = parseIPFSImageUrl(image.originalUrl); + const metadata = await ERC721Metadata.findOneAndUpdate( + { + erc721Id, + externalUrl: collection.externalUrl, + }, + { + erc721Id, + name, + description, + imageUrl, + image: imageUrl, + externalUrl: collection.externalUrl, + }, + { upsert: true, new: true }, + ); + const safe = await Wallet.findOne({ + address: req.body.address, + chainId: req.body.chainId, + }); + const token = await ERC721Token.findOneAndUpdate( + { tokenId, walletId: safe.id, erc721Id: erc721.id }, + { + walletId: safe.id, + erc721Id: erc721.id, + recipient: safe.address, + metadataId: metadata.id, + tokenUri, + tokenId, + state: ERC721TokenState.Minted, + }, + { upsert: true, new: true }, + ); + + return { ...token.toJSON(), metadata: metadata.toJSON() }; + } catch (error) { + console.log(error); + } + }), + ); + + res.status(201).json({ erc721, erc721Tokens }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/import/preview/post.controller.ts b/apps/api/src/app/controllers/erc721/import/preview/post.controller.ts new file mode 100644 index 000000000..ecc02ab36 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/import/preview/post.controller.ts @@ -0,0 +1,11 @@ +import { getNFTsForOwner } from '@thxnetwork/api/util/alchemy'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; + +const validation = [body('address').exists().isString(), body('chainId').exists().isInt()]; + +const controller = async (req: Request, res: Response) => { + const ownedNFTs = await getNFTsForOwner(req.body.address, req.body.contractAddress); + res.status(200).json(ownedNFTs); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/list.controller.ts b/apps/api/src/app/controllers/erc721/list.controller.ts new file mode 100644 index 000000000..6019f706d --- /dev/null +++ b/apps/api/src/app/controllers/erc721/list.controller.ts @@ -0,0 +1,11 @@ +import { Request, Response } from 'express'; +import { ERC721Document } from '@thxnetwork/api/models/ERC721'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const result = await ERC721Service.findBySub(req.auth.sub); + res.json(result.map((erc721: ERC721Document) => erc721._id)); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/metadata/delete.controller.ts b/apps/api/src/app/controllers/erc721/metadata/delete.controller.ts new file mode 100644 index 000000000..59fd840b7 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/delete.controller.ts @@ -0,0 +1,13 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; + +const validation = [param('metadataId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC721 Metadata'] + await ERC721Service.deleteMetadata(req.params.metadataId); + res.status(200).json({ success: true }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/metadata/erc721-metadata.test.ts b/apps/api/src/app/controllers/erc721/metadata/erc721-metadata.test.ts new file mode 100644 index 000000000..8fb8a2c4f --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/erc721-metadata.test.ts @@ -0,0 +1,129 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { createArchiver } from '@thxnetwork/api/util/zip'; +import { createImage } from '@thxnetwork/api/util/jest/images'; + +const user = request.agent(app); + +describe('ERC721 Metadata', () => { + let erc721ID: string, metadataId: string; + const chainId = ChainId.Hardhat, + name = 'Planets of the Galaxy', + symbol = 'GLXY', + description = 'Collection full of rarities.'; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /erc721', () => { + it('should create and return contract details', (done) => { + user.post('/v1/erc721') + .set('Authorization', dashboardAccessToken) + .send({ + chainId, + name, + symbol, + description, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(body.address).toBeDefined(); + erc721ID = body._id; + }) + .expect(201, done); + }); + }); + + describe('POST /erc721/:id/metadata', () => { + const name = 'red', + description = 'large', + imageUrl = 'http://imageURL.com', + externalUrl = 'http://externalurl.com'; + + it('HTTP 201', (done) => { + user.post('/v1/erc721/' + erc721ID + '/metadata') + .set('Authorization', dashboardAccessToken) + .send({ + name, + description, + imageUrl, + externalUrl, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(body.name).toBe(name); + expect(body.description).toBe(description); + expect(body.image).toBe(imageUrl); + expect(body.imageUrl).toBe(imageUrl); + expect(body.externalUrl).toBe(externalUrl); + metadataId = body._id; + }) + .expect(201, done); + }); + }); + + describe('PATCH /metadata/:metadataId', () => { + const value1 = 'blue', + value2 = 'small', + value3 = 'http://imageURL2.com', + value4 = 'http://externalurl2.com'; + + it('should return modified metadata for metadataId', (done) => { + user.patch('/v1/erc721/' + erc721ID + '/metadata/' + metadataId) + .set('Authorization', dashboardAccessToken) + .send({ + name: value1, + description: value2, + imageUrl: value3, + externalUrl: value4, + }) + .expect(({ body }: request.Response) => { + expect(body.name).toBe(value1); + expect(body.description).toBe(value2); + expect(body.image).toBe(value3); + expect(body.imageUrl).toBe(value3); + expect(body.externalUrl).toBe(value4); + }) + .expect(200, done); + }); + }); + + describe('POST /erc721/:id/metadata/zip', () => { + it('HTTP 201', async () => { + const image1 = createImage(); + const image2 = createImage(); + const image3 = createImage(); + const zip = createArchiver().jsZip; + const zipFolder = zip.folder('testImages'); + zipFolder.file('image1.jpg', image1, { binary: true }); + zipFolder.file('image2.jpg', image2, { binary: true }); + zipFolder.file('image3.jpg', image3, { binary: true }); + + const zipFile = await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' }); + await user + .post('/v1/erc721/' + erc721ID + '/metadata/zip') + .set('Authorization', dashboardAccessToken) + .attach('file', zipFile, { filename: 'images.zip', contentType: 'application/zip' }) + .field({ + description, + propName: 'image', + }) + .expect(201); + }); + }); + + describe('GET /metadata', () => { + it('HTTP 200', (done) => { + user.get('/v1/erc721/' + erc721ID + '/metadata') + .set('Authorization', dashboardAccessToken) + .expect(({ body }: request.Response) => { + expect(body.results.length).toBe(4); + expect(body.total).toBe(4); + }) + .expect(200, done); + }); + }); +}); diff --git a/apps/api/src/app/controllers/erc721/metadata/get.controller.ts b/apps/api/src/app/controllers/erc721/metadata/get.controller.ts new file mode 100644 index 000000000..b49b6c279 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/get.controller.ts @@ -0,0 +1,13 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; + +const validation = [param('id').isMongoId(), param('metadataId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC721 Metadata'] + const metadata = await ERC721Metadata.findById(req.params.metadataId); + res.json(metadata); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/metadata/images/post.controller.ts b/apps/api/src/app/controllers/erc721/metadata/images/post.controller.ts new file mode 100644 index 000000000..9c6f5b81d --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/images/post.controller.ts @@ -0,0 +1,97 @@ +import { AWS_S3_PUBLIC_BUCKET_NAME, IPFS_BASE_URL, NODE_ENV } from '@thxnetwork/api/config/secrets'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import ImageService from '@thxnetwork/api/services/ImageService'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { logger } from '@thxnetwork/api/util/logger'; +import { s3Client } from '@thxnetwork/api/util/s3'; +import { createArchiver } from '@thxnetwork/api/util/zip'; +import { PutObjectCommand } from '@aws-sdk/client-s3'; +import { Request, Response } from 'express'; +import { body, check, param } from 'express-validator'; +import short from 'short-uuid'; +import IPFSService from '@thxnetwork/api/services/IPFSService'; +import fileType from 'magic-bytes.js'; + +const validation = [ + param('id').isMongoId(), + body('propName').exists().isString(), + check('file').custom((value, { req }) => { + switch (req.file.mimetype) { + case 'application/octet-stream': + case 'application/zip': + case 'application/rar': + return true; + default: + return false; + } + }), +]; + +function parseFilename(filename: string, extension: string) { + return filename.toLowerCase().split(' ').join('-').split('.') + '-' + short.generate() + `.${extension}`; +} + +const controller = async (req: Request, res: Response) => { + const erc721 = await ERC721Service.findById(req.params.id); + if (!erc721) throw new NotFoundError('Could not find this NFT in the database'); + + const zip = createArchiver().jsZip; + const contents = await zip.loadAsync(req.file.buffer); + + for (const fileName of Object.keys(contents.files)) { + try { + const extension = fileName.substring(fileName.lastIndexOf('.')).substring(1); + if (!extension) continue; + + const originalFileName = fileName.substring(0, fileName.lastIndexOf('.')); + if (!isValidExtension(extension)) continue; + + const file = await zip.file(fileName).async('nodebuffer'); + const isValid = await isValidFileType(file); + if (!isValid) continue; + + const filename = parseFilename(originalFileName, extension); + await s3Client.send( + new PutObjectCommand({ + Key: filename, + Bucket: AWS_S3_PUBLIC_BUCKET_NAME, + ACL: 'public-read', + Body: file, + }), + ); + + const imageUrl = req.file && (await ImageService.upload(req.file)); + let image = imageUrl; + if (NODE_ENV === 'production') { + const cid = await IPFSService.addUrlSource(imageUrl); + image = IPFS_BASE_URL + cid; + } + + await ERC721Metadata.create({ + erc721Id: erc721.id, + name: req.body.name, + description: req.body.description, + externalUrl: req.body.externalUrl, + image, + imageUrl, + }); + } catch (err) { + console.log(err); + logger.error(err); + } + } + + res.status(201).end(); +}; + +function isValidExtension(extension: string) { + return ['jpg', 'jpeg', 'gif', 'png'].includes(extension); +} + +function isValidFileType(buffer: Buffer) { + const [type] = fileType(buffer); + return ['image/jpeg', 'image/png', 'image/gif'].includes(type.mime); +} + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/metadata/list.controller.ts b/apps/api/src/app/controllers/erc721/metadata/list.controller.ts new file mode 100644 index 000000000..995da7973 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/list.controller.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import { param, query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [ + param('id').isMongoId(), + query('limit').optional().isInt({ gt: 0 }), + query('page').optional().isInt({ gt: 0 }), +]; + +const controller = async (req: Request, res: Response) => { + const erc721 = await ERC721Service.findById(req.params.id); + if (!erc721) throw new NotFoundError('Could not find this NFT in the database'); + + const result = await ERC721Service.findMetadataByNFT( + erc721._id, + req.query.page ? Number(req.query.page) : null, + req.query.limit ? Number(req.query.limit) : null, + ); + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/metadata/patch.controller.ts b/apps/api/src/app/controllers/erc721/metadata/patch.controller.ts new file mode 100644 index 000000000..07d10cbd1 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/patch.controller.ts @@ -0,0 +1,45 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { BadRequestError, NotFoundError } from '@thxnetwork/api/util/errors'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import IPFSService from '@thxnetwork/api/services/IPFSService'; +import { IPFS_BASE_URL, NODE_ENV } from '@thxnetwork/api/config/secrets'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; + +const validation = [ + param('id').isMongoId(), + param('metadataId').isMongoId(), + body('name').optional().isString(), + body('description').optional().isString(), + body('externalUrl').optional().isURL(), + body('imageUrl').optional().isURL(), +]; + +const controller = async (req: Request, res: Response) => { + const erc721 = await ERC721Service.findById(req.params.id); + if (!erc721) throw new NotFoundError('Could not find this NFT in the database'); + + const metadata = await ERC721Metadata.findById(req.params.metadataId); + if (!metadata) throw new NotFoundError('Could not find this NFT Metadata in the database'); + + const tokens = metadata.tokens || []; + if (tokens.length) throw new BadRequestError('There token minted with this metadata'); + + let image = req.body.imageUrl; + if (req.body.imageUrl && NODE_ENV === 'production') { + const cid = await IPFSService.addUrlSource(req.body.imageUrl); + image = IPFS_BASE_URL + cid; + } + + metadata.name = req.body.name || metadata.name; + metadata.image = image || metadata.image; + metadata.imageUrl = req.body.imageUrl || metadata.imageUrl; + metadata.description = req.body.description || metadata.description; + metadata.externalUrl = req.body.externalUrl || metadata.externalUrl; + + await metadata.save(); + + res.json({ ...metadata.toJSON(), tokens }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/metadata/post.controller.ts b/apps/api/src/app/controllers/erc721/metadata/post.controller.ts new file mode 100644 index 000000000..a37dfdb2f --- /dev/null +++ b/apps/api/src/app/controllers/erc721/metadata/post.controller.ts @@ -0,0 +1,39 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; +import { IPFS_BASE_URL, NODE_ENV } from '@thxnetwork/api/config/secrets'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import IPFSService from '@thxnetwork/api/services/IPFSService'; + +const validation = [ + param('id').isMongoId(), + body('name').optional().isString(), + body('imageUrl').optional().isURL(), + body('description').optional().isString(), + body('externalUrl').optional().isURL(), +]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC721'] + const erc721 = await ERC721Service.findById(req.params.id); + if (!erc721) throw new NotFoundError('Could not find this NFT in the database'); + + let image = req.body.imageUrl; + if (req.body.imageUrl && NODE_ENV === 'production') { + const cid = await IPFSService.addUrlSource(req.body.imageUrl); + image = IPFS_BASE_URL + cid; + } + + const metadata = await ERC721Metadata.create({ + erc721Id: String(erc721._id), + name: req.body.name, + image, + imageUrl: req.body.imageUrl, + description: req.body.description, + externalUrl: req.body.externalUrl, + }); + + res.status(201).json(metadata); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/patch.controller.ts b/apps/api/src/app/controllers/erc721/patch.controller.ts new file mode 100644 index 000000000..77b4e9776 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/patch.controller.ts @@ -0,0 +1,19 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC721 } from '@thxnetwork/api/models/ERC721'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC721'] + const erc721 = await ERC721Service.findById(req.params.id); + if (!erc721) throw new NotFoundError('Could not find the token for this id'); + if (erc721.sub !== req.auth.sub) throw new ForbiddenError('Not your ERC721'); + + const result = await ERC721.findByIdAndUpdate(req.params.id, req.body, { new: true }); + + res.json(result); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/post.controller.ts b/apps/api/src/app/controllers/erc721/post.controller.ts new file mode 100644 index 000000000..b7c1d67fd --- /dev/null +++ b/apps/api/src/app/controllers/erc721/post.controller.ts @@ -0,0 +1,46 @@ +import { API_URL, IPFS_BASE_URL, VERSION } from '@thxnetwork/api/config/secrets'; +import { Request, Response } from 'express'; +import { body, check, query } from 'express-validator'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import ImageService from '@thxnetwork/api/services/ImageService'; +import { AccountPlanType, NFTVariant } from '@thxnetwork/common/enums'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const validation = [ + body('name').exists().isString(), + body('symbol').exists().isString(), + body('description').exists().isString(), + body('chainId').exists().isNumeric(), + check('file') + .optional() + .custom((value, { req }) => { + return ['jpg', 'jpeg', 'gif', 'png'].includes(req.file.mimetype); + }), + query('forceSync').optional().isBoolean(), +]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC721'] + + const logoImgUrl = req.file && (await ImageService.upload(req.file)); + const forceSync = req.query.forceSync !== undefined ? req.query.forceSync === 'true' : false; + const account = await AccountProxy.findById(req.auth.sub); + const baseURL = account.plan === AccountPlanType.Premium ? IPFS_BASE_URL : `${API_URL}/${VERSION}/metadata/`; + const erc721 = await ERC721Service.deploy( + { + variant: NFTVariant.ERC721, + sub: req.auth.sub, + chainId: req.body.chainId, + name: req.body.name, + symbol: req.body.symbol, + description: req.body.description, + baseURL, + logoImgUrl, + }, + forceSync, + ); + + res.status(201).json(erc721); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/token/get.controller.ts b/apps/api/src/app/controllers/erc721/token/get.controller.ts new file mode 100644 index 000000000..8f91f5b10 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/token/get.controller.ts @@ -0,0 +1,42 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { fromWei } from 'web3-utils'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; +import { ERC721Token } from '@thxnetwork/api/models/ERC721Token'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const token = await ERC721Token.findById(req.params.id); + if (!token) throw new NotFoundError('ERC721Token not found'); + + const [erc721, metadata] = await Promise.all([ + ERC721Service.findById(token.erc721Id), + ERC721Metadata.findById(token.metadataId), + ]); + if (!erc721) throw new NotFoundError('ERC721 not found'); + if (!metadata) throw new NotFoundError('ERC721Metadata not found'); + + const balanceInWei = await erc721.contract.methods.balanceOf(token.recipient).call(); + const balance = Number(fromWei(balanceInWei, 'ether')); + + const [owner, tokenUri] = token.tokenId + ? await Promise.all([ + erc721.contract.methods.ownerOf(token.tokenId).call(), + erc721.contract.methods.tokenURI(token.tokenId).call(), + ]) + : []; + + res.status(200).json({ + ...token.toJSON(), + owner, + tokenUri, + balance, + nft: erc721.toJSON(), + metadata: metadata.toJSON(), + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/token/list.controller.ts b/apps/api/src/app/controllers/erc721/token/list.controller.ts new file mode 100644 index 000000000..e54b15b81 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/token/list.controller.ts @@ -0,0 +1,30 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { ERC721Token, ERC721TokenDocument, ERC721Metadata } from '@thxnetwork/api/models'; +import { BadRequestError } from '@thxnetwork/api/util/errors'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [query('walletId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const wallet = await SafeService.findById(req.query.walletId as string); + if (!wallet) throw new BadRequestError('Wallet not found'); + + const tokens = await ERC721Token.find({ walletId: wallet.id }); + const result = await Promise.all( + tokens.map(async (token: ERC721TokenDocument) => { + const erc721 = await ERC721Service.findById(token.erc721Id); + if (!erc721) return; + + const metadata = await ERC721Metadata.findById(token.metadataId); + if (!metadata) return; + + return Object.assign(token.toJSON() as TERC721Token, { metadata, tokenUri: token.tokenUri, nft: erc721 }); + }), + ); + + res.json(result.reverse().filter((token) => !!token)); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/erc721/transfer/erc721-transfer.test.ts b/apps/api/src/app/controllers/erc721/transfer/erc721-transfer.test.ts new file mode 100644 index 000000000..451fc22c6 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/transfer/erc721-transfer.test.ts @@ -0,0 +1,191 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId, NFTVariant } from '@thxnetwork/common/enums'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { sub, sub2, userWalletPrivateKey, widgetAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { poll } from '@thxnetwork/api/util/polling'; +import { signTxHash } from '@thxnetwork/api/util/jest/network'; +import { safeVersion } from '@thxnetwork/api/services/ContractService'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { + ERC721Token, + ERC721TokenDocument, + ERC721, + ERC721Document, + ERC721Metadata, + Wallet, + WalletDocument, + Pool, + PoolDocument, +} from '@thxnetwork/api/models'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const user = request.agent(app); + +describe('ERC721 Transfer', () => { + let erc721: ERC721Document, + erc721Token: ERC721TokenDocument, + pool: PoolDocument, + wallet: WalletDocument, + safeTxHash = ''; + const chainId = ChainId.Hardhat, + name = 'Test Collection', + symbol = 'TST', + baseURL = 'https://example.com', + logoImgUrl = 'https://img.url', + metadataName = 'Testname', + metadataImageUrl = 'Testimageurl', + metadataIPFSImageUrl = 'TestIPFSimageurl', + metadataDescription = 'Testdescription', + metadataExternalUrl = 'TestexternalURL'; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + it('Deploy Campaign Safe', async () => { + const { web3 } = getProvider(chainId); + pool = await PoolService.deploy(sub, 'My Reward Campaign'); + const safe = await SafeService.create({ chainId, sub, safeVersion, poolId: String(pool._id) }); + + // Wait for safe address to return code + await poll( + () => web3.eth.getCode(safe.address), + (data: string) => data === '0x', + 1000, + ); + const code = await web3.eth.getCode(safe.address); + const result = code !== '0x'; + + expect(result).toBe(true); + }); + + it('Deploy ERC721', async () => { + const { web3 } = getProvider(chainId); + + erc721 = await ERC721Service.deploy( + { + variant: NFTVariant.ERC721, + sub, + chainId, + name, + symbol, + description: '', + baseURL, + archived: false, + logoImgUrl, + }, + true, + ); + + // Wait for nft address to return code + await poll( + async () => (await ERC721.findById(erc721._id)).address, + (address: string) => !address || !address.length, + 1000, + ); + + erc721 = await ERC721.findById(erc721._id); + + const code = await web3.eth.getCode(erc721.address); + const result = code !== '0x'; + expect(result).toBe(true); + }); + + it('Add ERC721 minter', async () => { + pool = await Pool.findById(pool._id); + + const safe = await SafeService.findOneByPool(pool); + erc721 = await ERC721.findById(erc721._id); + + await ERC721Service.addMinter(erc721, safe.address); + + // Wait for nft address to return code + await poll( + async () => await ERC721Service.isMinter(erc721, safe.address), + (isMinter: boolean) => !isMinter, + 1000, + ); + + const isMinter = await ERC721Service.isMinter(erc721, safe.address); + expect(isMinter).toBe(true); + }); + + it('Create ERC721 Metadata', async () => { + // Create metadata for token + const metadata = await ERC721Metadata.create({ + erc721Id: String(erc721._id), + name: metadataName, + image: metadataIPFSImageUrl, + imageUrl: metadataImageUrl, + description: metadataDescription, + externalUrl: metadataExternalUrl, + }); + const safe = await SafeService.findOneByPool(pool); + + // Wait for safe address to return code + const { web3 } = getProvider(chainId); + await poll( + () => web3.eth.getCode(safe.address), + (data: string) => data === '0x', + 1000, + ); + + wallet = await SafeService.findOne({ sub, safeVersion: { $exists: true } }); + + // Mint a token for metadata + erc721Token = await ERC721Service.mint(safe, erc721, wallet, metadata); + + // Wait for tokenId to be set in mint callback + await poll( + async () => (await ERC721Token.findById(erc721Token._id)).tokenId, + (tokenId?: number) => typeof tokenId === 'undefined', + 1000, + ); + + erc721Token = await ERC721Token.findById(erc721Token._id); + + expect(erc721Token.tokenId).toBeDefined(); + }); + + it('Transfer ERC721 ownership', async () => { + const receiver = await Wallet.findOne({ sub: sub2, safeVersion }); + const { status, body } = await user + .post('/v1/erc721/transfer') + .set({ Authorization: widgetAccessToken }) + .send({ + walletId: String(wallet._id), + erc721Id: erc721._id, + erc721TokenId: erc721Token._id, + to: receiver.address, + }); + + expect(status).toBe(201); + expect(body.safeTxHash).toBeDefined(); + + safeTxHash = body.safeTxHash; + }); + + it('Confirm tx', async () => { + const wallet = await SafeService.findOne({ sub, safeVersion: { $exists: true } }); + const { signature } = await signTxHash(wallet.address, safeTxHash, userWalletPrivateKey); + const { status, body } = await user + .post(`/v1/account/wallets/confirm`) + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(wallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash, signature }); + expect(status).toBe(200); + }); + + it('Wait for ownerOf', async () => { + const receiver = await Wallet.findOne({ sub: sub2, safeVersion }); + const token = await ERC721Token.findById(erc721Token._id); + const { contract } = await ERC721.findById(erc721._id); + + await poll(contract.methods.ownerOf(token.tokenId).call, (result: string) => result !== receiver.address, 1000); + + const owner = await contract.methods.ownerOf(token.tokenId).call(); + expect(owner).toEqual(receiver.address); + }); +}); diff --git a/apps/api/src/app/controllers/erc721/transfer/post.controller.ts b/apps/api/src/app/controllers/erc721/transfer/post.controller.ts new file mode 100644 index 000000000..a2c7f22e9 --- /dev/null +++ b/apps/api/src/app/controllers/erc721/transfer/post.controller.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC721Token } from '@thxnetwork/api/models/ERC721Token'; +import { ERC721 } from '@thxnetwork/api/models/ERC721'; +import { Transaction } from '@thxnetwork/api/models/Transaction'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [ + body('walletId').isMongoId(), + body('erc721Id').isMongoId(), + body('erc721TokenId').isMongoId(), + body('to').isString(), +]; + +const controller = async (req: Request, res: Response) => { + const erc721 = await ERC721.findById(req.body.erc721Id); + if (!erc721) throw new NotFoundError('Could not find the ERC721'); + + const erc721Token = await ERC721Token.findById(req.body.erc721TokenId); + if (!erc721Token) throw new NotFoundError('Could not find token for wallet'); + + const wallet = await SafeService.findById(req.body.walletId); + if (!wallet) throw new NotFoundError('Could not find wallet for account'); + + const owner = await erc721.contract.methods.ownerOf(erc721Token.tokenId).call(); + if (owner !== wallet.address) throw new ForbiddenError('Account is not owner of given tokenId'); + + const receiverToken = await ERC721Service.transferFrom(erc721, wallet, req.body.to, erc721Token); + const tx = await Transaction.findById(receiverToken.transactions[0]); + + res.status(201).json(tx); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/events/events.router.ts b/apps/api/src/app/controllers/events/events.router.ts new file mode 100644 index 000000000..b82941b96 --- /dev/null +++ b/apps/api/src/app/controllers/events/events.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import * as CreateEvents from './post.controller'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router(); + +router.post('/', guard.check(['events:write']), assertRequestInput(CreateEvents.validation), CreateEvents.controller); + +export default router; diff --git a/apps/api/src/app/controllers/events/post.controller.ts b/apps/api/src/app/controllers/events/post.controller.ts new file mode 100644 index 000000000..a6c806b00 --- /dev/null +++ b/apps/api/src/app/controllers/events/post.controller.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { Pool, Event, Identity, Client } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [body('event').isString().isLength({ min: 0, max: 50 }), body('identityUuid').isUUID()]; + +const controller = async (req: Request, res: Response) => { + const { identityUuid, event } = req.body; + const client = await Client.findOne({ clientId: req.auth.client_id }); + if (!client) throw new NotFoundError('Could not find client for token'); + + const pool = await Pool.findById(client.poolId); + if (!pool) throw new NotFoundError('Could not find pool for client'); + + const identity = await Identity.findOne({ uuid: identityUuid }); + if (!identity) throw new NotFoundError('Could not find ID for uuid'); + + await Event.create({ name: event, poolId: pool._id, identityId: identity._id }); + + res.status(201).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/health/health.router.ts b/apps/api/src/app/controllers/health/health.router.ts new file mode 100644 index 000000000..c973b0469 --- /dev/null +++ b/apps/api/src/app/controllers/health/health.router.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import * as ListHealth from './list.controller'; + +const router: express.Router = express.Router(); + +router.get('/', ListHealth.controller); + +export default router; diff --git a/apps/api/src/app/controllers/health/list.controller.ts b/apps/api/src/app/controllers/health/list.controller.ts new file mode 100644 index 000000000..5336c29fb --- /dev/null +++ b/apps/api/src/app/controllers/health/list.controller.ts @@ -0,0 +1,140 @@ +import { Request, Response } from 'express'; +import { fromWei } from 'web3-utils'; +import { NODE_ENV } from '@thxnetwork/api/config/secrets'; +import { ChainId } from '@thxnetwork/common/enums'; +import { logger } from '@thxnetwork/api/util/logger'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { ethers } from 'ethers'; +import { getArtifact, contractNetworks } from '@thxnetwork/api/hardhat'; +import { BigNumber } from 'alchemy-sdk'; + +function handleError(error: Error) { + logger.error(error); + return { error: 'invalid response' }; +} + +async function getNetworkDetails(chainId: ChainId) { + try { + const { defaultAccount, web3, signer } = getProvider(chainId); + const rfthx = new ethers.Contract( + contractNetworks[chainId].RewardFaucet, + getArtifact('RewardFaucet').abi, + signer, + ); + const registry = new ethers.Contract( + contractNetworks[chainId].THXRegistry, + getArtifact('THXRegistry').abi, + signer, + ); + const rdthx = new ethers.Contract( + contractNetworks[chainId].RewardDistributor, + getArtifact('RewardDistributor').abi, + signer, + ); + const bpt = new ethers.Contract(contractNetworks[chainId].BPT, getArtifact('BPT').abi, signer); + const bptGauge = new ethers.Contract(contractNetworks[chainId].BPTGauge, getArtifact('BPTGauge').abi, signer); + const veTHX = new ethers.Contract( + contractNetworks[chainId].VotingEscrow, + getArtifact('VotingEscrow').abi, + signer, + ); + const bal = new ethers.Contract(contractNetworks[chainId].BAL, getArtifact('BAL').abi, signer); + // const thx = new ethers.Contract(contractNetworks[chainId].THX, contractArtifacts['THX'].abi, signer); + // const usdc = new ethers.Contract(contractNetworks[chainId].USDC, contractArtifacts['USDC'].abi, signer); + + const address = { + registry: registry.address, + relayer: defaultAccount, + bptGauge: bptGauge.address, + bpt: await bpt.getAddress(), + bal: await bal.getAddress(), + thx: contractNetworks[chainId].THX, + usdc: contractNetworks[chainId].USDC, + vault: contractNetworks[chainId].BalancerVault, + }; + + const relayer = await Promise.all([ + { + matic: fromWei(String(await web3.eth.getBalance(defaultAccount)), 'ether'), + bpt: fromWei(String(await bpt.balanceOf(defaultAccount)), 'ether'), + // bptGauge: fromWei(String(await bptGauge.balanceOf(defaultAccount)), 'ether'), + // bal: fromWei(String(await bal.balanceOf(defaultAccount)), 'ether'), + // thx: fromWei(String(await thx.balanceOf(defaultAccount)), 'ether'), + // usdc: fromWei(String(await usdc.balanceOf(defaultAccount)), 'ether'), + }, + ]); + const total = fromWei(String(await rfthx.totalTokenRewards(bpt.address)), 'ether'); + const currentBlock = await web3.eth.getBlock('latest'); + const amountStaked = BigNumber.from(String(await bpt.balanceOf(bptGauge.address))); + const amountSupply = BigNumber.from(String(await bpt.totalSupply())); + const amountUnstaked = amountSupply.sub(amountStaked); + const amountLocked = await bptGauge.balanceOf(veTHX.address); + + const metrics = { + unstaked: fromWei(amountUnstaked.toString(), 'ether'), + staked: fromWei(amountStaked.toString(), 'ether'), + locked: fromWei(amountLocked.toString(), 'ether'), + }; + const getRewards = async (tokenAddress: string, now: string) => { + const currentWeek = fromWei(String(await rfthx.getTokenWeekAmounts(tokenAddress, now))); + const upcomingWeeks = (await rfthx.getUpcomingRewardsForNWeeks(tokenAddress, 4)).map((amount: BigNumber) => + fromWei(String(amount)), + ); + return [currentWeek, ...upcomingWeeks]; + }; + const distributor = { + total, + balances: await Promise.all([ + { + bpt: fromWei(String(await bpt.balanceOf(rfthx.address)), 'ether'), + bal: fromWei(String(await bal.balanceOf(rfthx.address)), 'ether'), + }, + ]), + rewards: { + bpt: await getRewards(await bpt.getAddress(), String(currentBlock.timestamp)), + bal: await getRewards(await bal.getAddress(), String(currentBlock.timestamp)), + }, + }; + const splitter = new ethers.Contract( + contractNetworks[chainId].THXPaymentSplitter, + getArtifact('THXPaymentSplitter').abi, + signer, + ); + + return { + blockTime: new Date(Number(currentBlock.timestamp) * 1000), + registry: { + payoutRate: BigNumber.from(await registry.getPayoutRate()) + .div(100) + .toString(), + payee: await registry.getPayee(), + }, + test: { + rate: (await splitter.rates('0x029E2d4D2b6938c92c48dbf422a4e500425a08D8')).toString(), + balance: (await splitter.balanceOf('0x029E2d4D2b6938c92c48dbf422a4e500425a08D8')).toString(), + }, + address, + relayer, + metrics, + distributor, + }; + } catch (error) { + return handleError(error); + } +} + +const controller = async (req: Request, res: Response) => { + const result = { + networks: {}, + }; + + if (NODE_ENV !== 'production') { + result.networks[ChainId.Hardhat] = await getNetworkDetails(ChainId.Hardhat); + } else { + result.networks[ChainId.Polygon] = await getNetworkDetails(ChainId.Polygon); + } + + res.header('Content-Type', 'application/json').send(JSON.stringify(result, null, 4)); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/identity/get.controller.ts b/apps/api/src/app/controllers/identity/get.controller.ts new file mode 100644 index 000000000..fccd85df0 --- /dev/null +++ b/apps/api/src/app/controllers/identity/get.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { Pool, Client } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { param } from 'express-validator'; +import IdentityService from '@thxnetwork/api/services/IdentityService'; + +const validation = [param('salt').isString().isLength({ min: 0 })]; + +const controller = async (req: Request, res: Response) => { + const client = await Client.findOne({ clientId: req.auth.client_id }); + if (!client) throw new NotFoundError('Could not find client for token'); + + const pool = await Pool.findById(client.poolId); + if (!pool) throw new NotFoundError('Could not find pool for client'); + + const identity = await IdentityService.getIdentityForSalt(pool, req.params.salt); + + res.json(identity.uuid); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/identity/identity.router.ts b/apps/api/src/app/controllers/identity/identity.router.ts new file mode 100644 index 000000000..832a074e8 --- /dev/null +++ b/apps/api/src/app/controllers/identity/identity.router.ts @@ -0,0 +1,23 @@ +import express from 'express'; +import * as CreateController from './post.controller'; +import * as UpdateController from './patch.controller'; +import * as ReadController from './get.controller'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router(); + +router.patch('/:uuid', assertRequestInput(UpdateController.validation), UpdateController.controller); +router.get( + '/:salt', + guard.check(['identities:read']), + assertRequestInput(ReadController.validation), + ReadController.controller, +); +router.post( + '/', + guard.check(['identities:write']), + assertRequestInput(CreateController.validation), + CreateController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/identity/patch.controller.ts b/apps/api/src/app/controllers/identity/patch.controller.ts new file mode 100644 index 000000000..07a5de856 --- /dev/null +++ b/apps/api/src/app/controllers/identity/patch.controller.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import { Pool, Identity } from '@thxnetwork/api/models'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { param } from 'express-validator'; + +const validation = [param('uuid').isUUID()]; + +const controller = async (req: Request, res: Response) => { + const pool = await Pool.findById(req.header('X-PoolId')); + if (!pool) throw new NotFoundError('Pool not found.'); + + const { uuid } = req.params; + const { sub } = req.auth; + + // Throw if Identity is connected already + const isConnected = await Identity.exists({ uuid, sub: { $exists: true } }); + if (isConnected) throw new ForbiddenError('Identity already connected.'); + + const identity = await Identity.findOneAndUpdate({ uuid }, { sub }, { new: true }); + + res.json(identity); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/identity/post.controller.ts b/apps/api/src/app/controllers/identity/post.controller.ts new file mode 100644 index 000000000..375901c07 --- /dev/null +++ b/apps/api/src/app/controllers/identity/post.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { Pool, Identity, Client } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { uuidV1 } from '@thxnetwork/api/util/uuid'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const client = await Client.findOne({ clientId: req.auth.client_id }); + if (!client) throw new NotFoundError('Could not find client for token'); + + const pool = await Pool.findById(client.poolId); + if (!pool) throw new NotFoundError('Could not find pool for client'); + + const uuid = uuidV1(); + const id = await Identity.create({ poolId: pool._id, uuid }); + + res.json(id.uuid); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/index.ts b/apps/api/src/app/controllers/index.ts new file mode 100644 index 000000000..73251ba9b --- /dev/null +++ b/apps/api/src/app/controllers/index.ts @@ -0,0 +1,67 @@ +import express from 'express'; +import RouterHealth from './health/health.router'; +import RouterAccount from './account/account.router'; +import RouterPools from './pools/pools.router'; +import RouterToken from './token/token.router'; +import RouterParticipants from './participants/participants.router'; +import RouterMetadata from './metadata/metadata.router'; +import RouterUpload from './upload/upload.router'; +import RouterERC20 from './erc20/erc20.router'; +import RouterERC721 from './erc721/erc721.router'; +import RouterERC1155 from './erc1155/erc1155.router'; +import RouterClients from './client/client.router'; +import RouterQRCodes from './qr-codes/qr-codes.router'; +import RouterBrands from './brands/brands.router'; +import RouterWidget from './widget/widget.router'; +import RouterQuests from './quests/quests.router'; +import RouterRewards from './rewards/rewards.router'; +import RouterLeaderboards from './leaderboards/leaderboards.router'; +import RouterWebhook from './webhook/webhook.router'; +import RouterWebhooks from './webhooks/webhooks.router'; +import RouterPrices from './earn/earn.router'; +import RouterWidgets from './widgets/widgets.router'; +import RouterIdentity from './identity/identity.router'; +import RouterEvents from './events/events.router'; +import RouterData from './data/data.router'; +import RouterLiquidity from './liquidity/liquidity.router'; +import RouterVoteEscrow from './ve/ve.router'; +import RouterJobs from './jobs/jobs.router'; +import RouterCoupons from './coupons/coupons.router'; +import { checkJwt, corsHandler } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.use('/ping', (_req, res) => res.send('pong')); +router.use('/health', RouterHealth); +router.use('/data', RouterData); +router.use('/token', RouterToken); +router.use('/metadata', RouterMetadata); +router.use('/brands', RouterBrands); +router.use('/widget', RouterWidget); +router.use('/leaderboards', RouterLeaderboards); +router.use('/claims', RouterQRCodes); // Legacy QR codes still redirect to /claims/r/:uuid +router.use('/qr-codes', RouterQRCodes); +router.use('/quests', RouterQuests); +router.use('/rewards', RouterRewards); +router.use('/webhook', RouterWebhook); +router.use('/earn', RouterPrices); +router.use(checkJwt, corsHandler); +router.use('/jobs', RouterJobs); +router.use('/upload', RouterUpload); +router.use('/identity', RouterIdentity); +router.use('/events', RouterEvents); +router.use('/coupons', RouterCoupons); +router.use('/account', RouterAccount); +router.use('/participants', RouterParticipants); +router.use('/pools', RouterPools); +router.use('/widgets', RouterWidgets); +router.use('/clients', RouterClients); +router.use('/webhooks', RouterWebhooks); +router.use('/ve', RouterVoteEscrow); +router.use('/liquidity', RouterLiquidity); + +router.use('/erc20', RouterERC20); +router.use('/erc721', RouterERC721); +router.use('/erc1155', RouterERC1155); + +export { router }; diff --git a/apps/api/src/app/controllers/jobs/get.controller.ts b/apps/api/src/app/controllers/jobs/get.controller.ts new file mode 100644 index 000000000..38a5a45af --- /dev/null +++ b/apps/api/src/app/controllers/jobs/get.controller.ts @@ -0,0 +1,12 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Job } from '@thxnetwork/api/models/Job'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const job = await Job.findById(req.params.id); + res.json(job); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/jobs/jobs.router.ts b/apps/api/src/app/controllers/jobs/jobs.router.ts new file mode 100644 index 000000000..a2d23db45 --- /dev/null +++ b/apps/api/src/app/controllers/jobs/jobs.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import * as ReadJobs from './get.controller'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router(); + +router.get( + '/:id', + guard.check([ + // 'jobs:read' + ]), + assertRequestInput(ReadJobs.validation), + ReadJobs.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/leaderboards/get.controller.ts b/apps/api/src/app/controllers/leaderboards/get.controller.ts new file mode 100644 index 000000000..4416f5bfe --- /dev/null +++ b/apps/api/src/app/controllers/leaderboards/get.controller.ts @@ -0,0 +1,25 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [param('campaignId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.campaignId); + const leaderboard = await PoolService.findParticipants(pool, 1, 10); + const result = leaderboard.results.map((p) => { + return { + rank: p.rank, + account: { + username: p.account && p.account.username, + profileImg: p.account && p.account.profileImg, + }, + questsCompleted: p.questEntryCount, + score: p.score, + }; + }); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/leaderboards/leaderboards.router.ts b/apps/api/src/app/controllers/leaderboards/leaderboards.router.ts new file mode 100644 index 000000000..cd1648e4a --- /dev/null +++ b/apps/api/src/app/controllers/leaderboards/leaderboards.router.ts @@ -0,0 +1,11 @@ +import express from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as ListLeaderboard from './list.controller'; +import * as ReadLeaderboard from './get.controller'; + +export const router: express.Router = express.Router(); + +router.get('/', assertRequestInput(ListLeaderboard.validation), ListLeaderboard.controller); +router.get('/:campaignId', assertRequestInput(ReadLeaderboard.validation), ReadLeaderboard.controller); + +export default router; diff --git a/apps/api/src/app/controllers/leaderboards/list.controller.ts b/apps/api/src/app/controllers/leaderboards/list.controller.ts new file mode 100644 index 000000000..990bf9649 --- /dev/null +++ b/apps/api/src/app/controllers/leaderboards/list.controller.ts @@ -0,0 +1,67 @@ +import { Request, Response } from 'express'; +import { Pool, PoolDocument, Brand } from '@thxnetwork/api/models'; +import { Widget } from '@thxnetwork/api/models/Widget'; +import { query } from 'express-validator'; +import { Participant } from '@thxnetwork/api/models/Participant'; +import RewardService from '@thxnetwork/api/services/RewardService'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const matchTitle = (search) => { + if (!search || !search.length) return; + return new RegExp( + search + .split(/\s+/) + .map((word) => `(?=.*${word})`) + .join(''), + 'i', + ); +}; + +export const paginatedResults = async (page: number, limit: number, search: string) => { + const startIndex = (page - 1) * limit; + const $match = { + 'rank': { $exists: true }, + 'settings.isPublished': true, + ...(search && { 'settings.title': matchTitle(search) }), + }; + const total = await Pool.countDocuments($match); + const results = await Pool.find($match).sort({ rank: 1 }).skip(startIndex).limit(limit); + + return { page, total, limit, results }; +}; + +const validation = [query('page').isInt(), query('limit').isInt(), query('search').optional().isString()]; + +const controller = async (req: Request, res: Response) => { + const { page, limit, search } = req.query; + const result = await paginatedResults(Number(page), Number(limit), search ? String(search) : ''); + const widgets = await Widget.find({ poolId: result.results.map((p: PoolDocument) => p._id) }); + const brands = await Brand.find({ poolId: result.results.map((p: PoolDocument) => p._id) }); + + result.results = (await Promise.all( + result.results.map(async (pool) => { + const widget = widgets.find((w) => w.poolId === String(pool._id)); + const brand = brands.find((b) => b.poolId === String(pool._id)); + const participantCount = await Participant.countDocuments({ poolId: pool._id }); + const questCount = await QuestService.count({ poolId: pool._id }); + const rewardCount = await RewardService.count({ poolId: pool._id }); + return { + _id: pool._id, + rank: pool.rank, + slug: pool.settings.slug || pool._id, + title: pool.settings.title, + domain: widget ? widget.domain : 'https://app.thx.network', + logoImgUrl: brand && brand.logoImgUrl, + backgroundImgUrl: brand && brand.backgroundImgUrl, + participantCount, + questCount, + rewardCount, + createdAt: pool.createdAt, + }; + }), + )) as any; + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/liquidity/liquidity.router.ts b/apps/api/src/app/controllers/liquidity/liquidity.router.ts new file mode 100644 index 000000000..49a74f5f5 --- /dev/null +++ b/apps/api/src/app/controllers/liquidity/liquidity.router.ts @@ -0,0 +1,12 @@ +import express from 'express'; +import { assertWallet, assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as CreateLiquidity from './post.controller'; +import * as CreateLiquidityStaked from './stake/post.controller'; + +const router: express.Router = express.Router(); + +router.use('/', assertWallet); +router.post('/', assertRequestInput(CreateLiquidity.validation), CreateLiquidity.controller); +router.post('/stake', assertRequestInput(CreateLiquidityStaked.validation), CreateLiquidityStaked.controller); + +export default router; diff --git a/apps/api/src/app/controllers/liquidity/post.controller.ts b/apps/api/src/app/controllers/liquidity/post.controller.ts new file mode 100644 index 000000000..0abffce3e --- /dev/null +++ b/apps/api/src/app/controllers/liquidity/post.controller.ts @@ -0,0 +1,17 @@ +import LiquidityService from '@thxnetwork/api/services/LiquidityService'; +import { Request, Response } from 'express'; +import { query, body } from 'express-validator'; + +const validation = [ + body('usdcAmountInWei').isString(), + body('thxAmountInWei').isString(), + body('slippage').isString(), + query('walletId').isMongoId(), +]; + +const controller = async ({ wallet, body }: Request, res: Response) => { + const tx = await LiquidityService.create(wallet, body.usdcAmountInWei, body.thxAmountInWei, body.slippage); + + res.status(201).json([tx]); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/liquidity/stake/post.controller.ts b/apps/api/src/app/controllers/liquidity/stake/post.controller.ts new file mode 100644 index 000000000..8abe0ad29 --- /dev/null +++ b/apps/api/src/app/controllers/liquidity/stake/post.controller.ts @@ -0,0 +1,26 @@ +import { getArtifact } from '@thxnetwork/api/hardhat'; +import { BadRequestError } from '@thxnetwork/api/util/errors'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { contractNetworks } from '@thxnetwork/api/hardhat'; +import { Request, Response } from 'express'; +import { body, query } from 'express-validator'; +import { BigNumber } from 'alchemy-sdk'; +import LiquidityService from '@thxnetwork/api/services/LiquidityService'; + +const validation = [body('amountInWei').isString(), query('walletId').isMongoId()]; + +const controller = async ({ wallet, body }: Request, res: Response) => { + const { web3 } = getProvider(wallet.chainId); + const bpt = new web3.eth.Contract(getArtifact('BPT').abi, contractNetworks[wallet.chainId].BPT); + + // Check if sender has sufficient BPT + const balanceInWei = await bpt.methods.balanceOf(wallet.address).call(); + if (BigNumber.from(balanceInWei).lt(body.amountInWei)) { + throw new BadRequestError('Insufficient balance'); + } + + const tx = await LiquidityService.stake(wallet, body.amountInWei); + + res.status(201).json([tx]); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/metadata/erc1155/get.controller.ts b/apps/api/src/app/controllers/metadata/erc1155/get.controller.ts new file mode 100644 index 000000000..a5c128aab --- /dev/null +++ b/apps/api/src/app/controllers/metadata/erc1155/get.controller.ts @@ -0,0 +1,24 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC1155Metadata } from '@thxnetwork/api/models/ERC1155Metadata'; + +const validation = [param('erc1155Id').isMongoId(), param('tokenId').isInt()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC1155 Metadata'] + const { erc1155Id, tokenId } = req.params; + const metadata = await ERC1155Metadata.findOne({ erc1155Id, tokenId }); + if (!metadata) throw new NotFoundError('Could not find metadata for this ID'); + + const attributes = { + name: metadata.name, + description: metadata.description, + image: metadata.image, + external_url: metadata.externalUrl, + }; + + res.header('Content-Type', 'application/json').send(JSON.stringify(attributes, null, 4)); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/metadata/get.controller.ts b/apps/api/src/app/controllers/metadata/get.controller.ts new file mode 100644 index 000000000..b89bf0067 --- /dev/null +++ b/apps/api/src/app/controllers/metadata/get.controller.ts @@ -0,0 +1,23 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { ERC721Metadata } from '@thxnetwork/api/models/ERC721Metadata'; + +const validation = [param('metadataId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['ERC721 Metadata'] + const metadata = await ERC721Metadata.findById(req.params.metadataId); + if (!metadata) throw new NotFoundError('Could not find metadata for this ID'); + + const attributes = { + name: metadata.name, + description: metadata.description, + image: metadata.image, + external_url: metadata.externalUrl, + }; + + res.header('Content-Type', 'application/json').send(JSON.stringify(attributes, null, 4)); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/metadata/metadata.router.ts b/apps/api/src/app/controllers/metadata/metadata.router.ts new file mode 100644 index 000000000..49d43904f --- /dev/null +++ b/apps/api/src/app/controllers/metadata/metadata.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as ReadMetadata from './get.controller'; +import * as ReadERC1155Metadata from './erc1155/get.controller'; + +const router: express.Router = express.Router(); + +router.get( + '/erc1155/:erc1155Id/:tokenId', + assertRequestInput(ReadERC1155Metadata.validation), + ReadERC1155Metadata.controller, +); +router.get('/:metadataId', assertRequestInput(ReadMetadata.validation), ReadMetadata.controller); + +export default router; diff --git a/apps/api/src/app/controllers/participants/get.controller.ts b/apps/api/src/app/controllers/participants/get.controller.ts new file mode 100644 index 000000000..0e649e376 --- /dev/null +++ b/apps/api/src/app/controllers/participants/get.controller.ts @@ -0,0 +1,38 @@ +import { Request, Response } from 'express'; +import { Participant } from '@thxnetwork/api/models/Participant'; +import { query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import IdentityService from '@thxnetwork/api/services/IdentityService'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [query('poolId').optional().isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const poolId = req.query.poolId as string; + const query: { sub: string; poolId?: string } = { sub: req.auth.sub }; + if (poolId) query.poolId = poolId; + + // Get all participants for the authenticated user and optionally filter by poolId + const participants = await Participant.find(query); + + // Run pool specific operations + if (poolId) { + const pool = await PoolService.getById(poolId); + const account = await AccountProxy.findById(req.auth.sub); + if (!account) throw new NotFoundError('Account not found.'); + + // Force connect account address as identity might be available + await IdentityService.forceConnect(pool, account); + + // If no participants were found, create a participant for the authenticated user + if (!participants.length) { + const participant = await Participant.create({ poolId, sub: account.sub }); + participants.push(participant); + } + } + + res.json(participants); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/participants/participants.router.ts b/apps/api/src/app/controllers/participants/participants.router.ts new file mode 100644 index 000000000..d4e914a45 --- /dev/null +++ b/apps/api/src/app/controllers/participants/participants.router.ts @@ -0,0 +1,21 @@ +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import express from 'express'; +import * as ListParticipants from './get.controller'; +import * as UpdateParticipants from './patch.controller'; + +const router: express.Router = express.Router(); + +router.get( + '/', + guard.check(['point_balances:read']), + assertRequestInput(ListParticipants.validation), + ListParticipants.controller, +); +router.patch( + '/:id', + guard.check(['point_balances:read']), + assertRequestInput(UpdateParticipants.validation), + UpdateParticipants.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/participants/patch.controller.ts b/apps/api/src/app/controllers/participants/patch.controller.ts new file mode 100644 index 000000000..4e06eb598 --- /dev/null +++ b/apps/api/src/app/controllers/participants/patch.controller.ts @@ -0,0 +1,31 @@ +import { Request, Response } from 'express'; +import { Participant } from '@thxnetwork/api/models/Participant'; +import { body, param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const validation = [ + param('id').isMongoId(), + body('isSubscribed').optional().isBoolean(), + body('email').optional().isEmail(), +]; + +const controller = async (req: Request, res: Response) => { + const participant = await Participant.findById(req.params.id); + if (!participant) throw new NotFoundError('Participant not found.'); + + // If subscribed is true and email we set the participant flag to true and patch the account + if (req.body.isSubscribed && req.body.email) { + const isSubscribed = JSON.parse(req.body.isSubscribed); + + if (isSubscribed) { + await AccountProxy.update(req.auth.sub, { email: String(req.body.email) } as TAccount); + } + + await participant.updateOne({ isSubscribed }); + } + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/analytics/analytics.router.ts b/apps/api/src/app/controllers/pools/analytics/analytics.router.ts new file mode 100644 index 000000000..ceb3dda7e --- /dev/null +++ b/apps/api/src/app/controllers/pools/analytics/analytics.router.ts @@ -0,0 +1,18 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import RouterMetrics from './metrics/metrics.router'; +import * as ListAnalytics from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListAnalytics.validation), + ListAnalytics.controller, +); + +router.use('/metrics', RouterMetrics); + +export default router; diff --git a/apps/api/src/app/controllers/pools/analytics/list.controller.ts b/apps/api/src/app/controllers/pools/analytics/list.controller.ts new file mode 100644 index 000000000..dc912070f --- /dev/null +++ b/apps/api/src/app/controllers/pools/analytics/list.controller.ts @@ -0,0 +1,17 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import AnalyticsService from '@thxnetwork/api/services/AnalyticsService'; + +const validation = [param('id').isMongoId(), query('startDate').exists(), query('endDate').exists()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + const startDate = new Date(String(req.query.startDate)); + const endDate = new Date(String(req.query.endDate)); + const result = await AnalyticsService.getPoolAnalyticsForChart(pool, startDate, endDate); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/analytics/metrics/list.controller.ts b/apps/api/src/app/controllers/pools/analytics/metrics/list.controller.ts new file mode 100644 index 000000000..0c8b55f71 --- /dev/null +++ b/apps/api/src/app/controllers/pools/analytics/metrics/list.controller.ts @@ -0,0 +1,20 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import AnalyticsService from '@thxnetwork/api/services/AnalyticsService'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { Participant } from '@thxnetwork/api/models/Participant'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Pools'] + const pool = await PoolService.getById(req.params.id); + const metrics = await AnalyticsService.getPoolMetrics(pool); + const participantCount = await Participant.countDocuments({ poolId: pool._id }); + const participantActiveCount = await Participant.countDocuments({ poolId: pool._id, score: { $gt: 0 } }); + const subscriptionCount = await Participant.countDocuments({ poolId: pool._id, isSubscribed: true }); + + res.json({ _id: pool._id, participantCount, participantActiveCount, subscriptionCount, ...metrics }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/analytics/metrics/metrics.router.ts b/apps/api/src/app/controllers/pools/analytics/metrics/metrics.router.ts new file mode 100644 index 000000000..789d44352 --- /dev/null +++ b/apps/api/src/app/controllers/pools/analytics/metrics/metrics.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ListMetrics from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListMetrics.validation), + ListMetrics.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/collaborators/collaborators.router.ts b/apps/api/src/app/controllers/pools/collaborators/collaborators.router.ts new file mode 100644 index 000000000..bf3469c98 --- /dev/null +++ b/apps/api/src/app/controllers/pools/collaborators/collaborators.router.ts @@ -0,0 +1,30 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as CreateController from './post.controller'; +import * as UpdateController from './patch.controller'; +import * as RemoveController from './delete.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post( + '/', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.patch( + '/:uuid', + guard.check(['pools:read', 'pools:write']), + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/:uuid', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(RemoveController.validation), + RemoveController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/collaborators/delete.controller.ts b/apps/api/src/app/controllers/pools/collaborators/delete.controller.ts new file mode 100644 index 000000000..9689f1a92 --- /dev/null +++ b/apps/api/src/app/controllers/pools/collaborators/delete.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { Collaborator } from '@thxnetwork/api/models/Collaborator'; + +const validation = [param('id').isMongoId(), param('uuid').isUUID(4)]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Pools'] + const pool = await PoolService.getById(req.params.id); + const collaborator = await Collaborator.findOne({ poolId: pool._id, uuid: req.params.uuid }); + if (!collaborator) throw new NotFoundError('Could not find collaborator'); + if (collaborator.sub === pool.sub) throw new ForbiddenError('Can not remove campaign owner'); + + await collaborator.deleteOne(); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/collaborators/patch.controller.ts b/apps/api/src/app/controllers/pools/collaborators/patch.controller.ts new file mode 100644 index 000000000..ba372cac7 --- /dev/null +++ b/apps/api/src/app/controllers/pools/collaborators/patch.controller.ts @@ -0,0 +1,26 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Collaborator } from '@thxnetwork/api/models/Collaborator'; +import { CollaboratorInviteState } from '@thxnetwork/common/enums'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId(), param('uuid').isUUID(4)]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Pools'] + const pool = await PoolService.getById(req.params.id); + const collaborator = await Collaborator.findOne({ poolId: req.params.id, uuid: req.params.uuid }); + if (!collaborator) throw new NotFoundError('Could not find collaboration invite'); + + if (pool.sub !== req.body.sub) { + await collaborator.updateOne({ + sub: req.auth.sub, + state: CollaboratorInviteState.Accepted, + }); + } + + res.end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/collaborators/post.controller.ts b/apps/api/src/app/controllers/pools/collaborators/post.controller.ts new file mode 100644 index 000000000..74fbb57e4 --- /dev/null +++ b/apps/api/src/app/controllers/pools/collaborators/post.controller.ts @@ -0,0 +1,14 @@ +import { Request, Response } from 'express'; +import { param, body } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [param('id').isMongoId(), body('email').isEmail()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + const collaborator = await PoolService.inviteCollaborator(pool, req.body.email); + + res.json(collaborator); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/delete.controller.ts b/apps/api/src/app/controllers/pools/delete.controller.ts new file mode 100644 index 000000000..14b773433 --- /dev/null +++ b/apps/api/src/app/controllers/pools/delete.controller.ts @@ -0,0 +1,18 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Pool } from '@thxnetwork/api/models'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find pool for this ID'); + + await Pool.deleteOne({ _id: pool._id }); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/erc1155/balance/balance.router.ts b/apps/api/src/app/controllers/pools/erc1155/balance/balance.router.ts new file mode 100644 index 000000000..1e4d828ad --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc1155/balance/balance.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import * as ReadBalances from './get.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', guard.check(['erc1155:read']), assertRequestInput(ReadBalances.validation), ReadBalances.controller); + +export default router; diff --git a/apps/api/src/app/controllers/pools/erc1155/balance/get.controller.ts b/apps/api/src/app/controllers/pools/erc1155/balance/get.controller.ts new file mode 100644 index 000000000..f8c0d37a5 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc1155/balance/get.controller.ts @@ -0,0 +1,22 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Pool } from '@thxnetwork/api/models'; +import ContractService from '@thxnetwork/api/services/ContractService'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [query('contractAddress').isEthereumAddress(), query('tokenId').isInt()]; + +const controller = async (req: Request, res: Response) => { + const pool = await Pool.findById(req.params.id); + if (!pool) throw new NotFoundError('Pool not found'); + + const safe = await SafeService.findOneByPool(pool, pool.chainId); + if (!safe) throw new NotFoundError('Safe not found'); + + const contract = ContractService.getContract('THXERC1155', pool.chainId, req.query.contractAddress as string); + const balance = await contract.balanceOf(safe.address, req.query.tokenId); + + res.json({ balance }); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/erc1155/erc1155.router.ts b/apps/api/src/app/controllers/pools/erc1155/erc1155.router.ts new file mode 100644 index 000000000..3808e042a --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc1155/erc1155.router.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import RouterBalance from './balance/balance.router'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.use('/balance', RouterBalance); + +export default router; diff --git a/apps/api/src/app/controllers/pools/erc20/allowance/allowance.router.ts b/apps/api/src/app/controllers/pools/erc20/allowance/allowance.router.ts new file mode 100644 index 000000000..81d369661 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc20/allowance/allowance.router.ts @@ -0,0 +1,11 @@ +import express from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as ListAllowances from './get.controller'; +import * as CreateAllowances from './post.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', assertRequestInput(ListAllowances.validation), ListAllowances.controller); +router.post('/', assertRequestInput(CreateAllowances.validation), CreateAllowances.controller); + +export default router; diff --git a/apps/api/src/app/controllers/pools/erc20/allowance/get.controller.ts b/apps/api/src/app/controllers/pools/erc20/allowance/get.controller.ts new file mode 100644 index 000000000..3c880a287 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc20/allowance/get.controller.ts @@ -0,0 +1,31 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import ContractService from '@thxnetwork/api/services/ContractService'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [ + param('id').isMongoId(), + query('tokenAddress').isEthereumAddress(), + query('spender').isEthereumAddress(), +]; + +const controller = async (req: Request, res: Response) => { + const poolId = req.params.id as string; + const pool = await PoolService.getById(poolId); + if (!pool) throw new NotFoundError('Pool not found'); + + const safe = await SafeService.findOneByPool(pool); + if (!safe) throw new NotFoundError('Wallet not found'); + + const contract = ContractService.getContract( + 'THXERC20_LimitedSupply', + safe.chainId, + req.query.tokenAddress as string, + ); + const allowance = await contract.allowance(safe.address, req.query.spender); + + res.json({ allowanceInWei: allowance.toString() }); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/erc20/allowance/post.controller.ts b/apps/api/src/app/controllers/pools/erc20/allowance/post.controller.ts new file mode 100644 index 000000000..7b2fb3a41 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc20/allowance/post.controller.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { getArtifact } from '@thxnetwork/api/hardhat'; +import TransactionService from '@thxnetwork/api/services/TransactionService'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [ + param('id').isMongoId(), + body('tokenAddress').isEthereumAddress(), + body('spender').isEthereumAddress(), + body('amountInWei').isString(), +]; + +const controller = async (req: Request, res: Response) => { + const poolId = req.params.id as string; + const pool = await PoolService.getById(poolId); + if (!pool) throw new NotFoundError('Pool not found'); + + const safe = await SafeService.findOneByPool(pool); + if (!safe) throw new NotFoundError('Wallet not found'); + + const { web3 } = getProvider(safe.chainId); + const { abi } = getArtifact('THXERC20_LimitedSupply'); + const contract = new web3.eth.Contract(abi, req.body.tokenAddress); + const fn = contract.methods.approve(req.body.spender, req.body.amountInWei); + + // Propose tx data to relayer and return safeTxHash to track status + const tx = await TransactionService.sendSafeAsync(safe, contract.options.address, fn); + + res.status(201).json([tx]); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/erc20/balance/balance.router.ts b/apps/api/src/app/controllers/pools/erc20/balance/balance.router.ts new file mode 100644 index 000000000..ceb7452d8 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc20/balance/balance.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ReadBalances from './get.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(ReadBalances.validation), + ReadBalances.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/erc20/balance/get.controller.ts b/apps/api/src/app/controllers/pools/erc20/balance/get.controller.ts new file mode 100644 index 000000000..e93e318c8 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc20/balance/get.controller.ts @@ -0,0 +1,27 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import ContractService from '@thxnetwork/api/services/ContractService'; + +const validation = [param('id').isMongoId(), query('tokenAddress').isEthereumAddress()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Campaign not found.'); + + const safe = await SafeService.findOneByPool(pool, pool.chainId); + if (!safe) throw new NotFoundError('Campaign Safe not found.'); + + const contract = ContractService.getContract( + 'THXERC20_LimitedSupply', + safe.chainId, + req.query.tokenAddress as string, + ); + const balance = await contract.balanceOf(safe.address); + + res.json({ balanceInWei: balance.toString() }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/erc20/erc20.router.ts b/apps/api/src/app/controllers/pools/erc20/erc20.router.ts new file mode 100644 index 000000000..6f3d56ca9 --- /dev/null +++ b/apps/api/src/app/controllers/pools/erc20/erc20.router.ts @@ -0,0 +1,10 @@ +import express from 'express'; +import RouterBalance from './balance/balance.router'; +import RouterAllowance from './allowance/allowance.router'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.use('/balance', RouterBalance); +router.use('/allowance', RouterAllowance); + +export default router; diff --git a/apps/api/src/app/controllers/pools/events/events.router.ts b/apps/api/src/app/controllers/pools/events/events.router.ts new file mode 100644 index 000000000..da785004e --- /dev/null +++ b/apps/api/src/app/controllers/pools/events/events.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ListEvents from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListEvents.validation), + ListEvents.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/events/list.controller.ts b/apps/api/src/app/controllers/pools/events/list.controller.ts new file mode 100644 index 000000000..4077fd4ca --- /dev/null +++ b/apps/api/src/app/controllers/pools/events/list.controller.ts @@ -0,0 +1,29 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Event, EventDocument } from '@thxnetwork/api/models/Event'; +import { paginatedResults } from '@thxnetwork/api/util/pagination'; +import { Identity } from '@thxnetwork/api/models/Identity'; +import { Pool } from '@thxnetwork/api/models'; + +const validation = [query('page').isInt(), query('limit').isInt()]; + +const controller = async (req: Request, res: Response) => { + const pool = await Pool.findById(req.header('X-PoolId')); + if (!pool) throw new NotFoundError('Could not find pool for token'); + + const result = await paginatedResults(Event, Number(req.query.page), Number(req.query.limit), { + poolId: pool._id, + }); + + result.results = await Promise.all( + result.results.map(async (event: EventDocument) => { + const identity = await Identity.findById(event.identityId); + return { ...event.toJSON(), identity }; + }), + ); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/get.controller.ts b/apps/api/src/app/controllers/pools/get.controller.ts new file mode 100644 index 000000000..ab7b97c81 --- /dev/null +++ b/apps/api/src/app/controllers/pools/get.controller.ts @@ -0,0 +1,65 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Participant, Widget, Wallet, Event, Identity } from '@thxnetwork/api/models'; +import { safeVersion } from '@thxnetwork/api/services/ContractService'; +import { logger } from '@thxnetwork/api/util/logger'; +import { ethers } from 'ethers'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import BrandService from '@thxnetwork/api/services/BrandService'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import PaymentService from '@thxnetwork/api/services/PaymentService'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + let safe = await SafeService.findOneByPool(pool, pool.chainId); + + // Deploy a Safe if none is found + if (!safe) { + safe = await SafeService.create({ + chainId: pool.chainId, + sub: pool.sub, + safeVersion, + poolId: req.params.id, + }); + logger.info(`[${req.params.id}] Deployed Campaign Safe ${safe.address}`); + } + + // Create a galachain private key if none exists + if (!pool.settings.galachainPrivateKey) { + const privateKey = ethers.Wallet.createRandom().privateKey; + await pool.updateOne({ 'settings.galachainPrivateKey': privateKey }); + } + + // Fetch all other campaign entities + const [widget, brand, wallets, collaborators, owner, events, identities, subscriberCount, balance] = + await Promise.all([ + Widget.findOne({ poolId: req.params.id }), + BrandService.get(req.params.id), + Wallet.find({ poolId: req.params.id }), + PoolService.findCollaborators(pool), + PoolService.findOwner(pool), + Event.find({ poolId: pool._id }).distinct('name'), // Seperate list (many) + Identity.find({ poolId: pool._id }), // Seperate list (many) + Participant.countDocuments({ poolId: req.params.id, isSubscribed: true }), + PaymentService.balanceOf(safe), + ]); + + res.json({ + ...pool.toJSON(), + balance, + address: pool.safeAddress, + safe, + identities, + events, + wallets, + widget, + brand, + subscriberCount, + owner, + collaborators, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/guilds/delete.controller.ts b/apps/api/src/app/controllers/pools/guilds/delete.controller.ts new file mode 100644 index 000000000..10f8e185c --- /dev/null +++ b/apps/api/src/app/controllers/pools/guilds/delete.controller.ts @@ -0,0 +1,12 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { DiscordGuild } from '@thxnetwork/api/models'; + +const validation = [param('id').isMongoId(), param('guildId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + await DiscordGuild.findByIdAndDelete(req.params.guildId); + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/guilds/guilds.router.ts b/apps/api/src/app/controllers/pools/guilds/guilds.router.ts new file mode 100644 index 000000000..8563b01d2 --- /dev/null +++ b/apps/api/src/app/controllers/pools/guilds/guilds.router.ts @@ -0,0 +1,39 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as CreateController from './post.controller'; +import * as UpdateController from './patch.controller'; +import * as RemoveController from './delete.controller'; +import * as ListController from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListController.validation), + ListController.controller, +); +router.post( + '/', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.patch( + '/:guildId', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/:guildId', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(RemoveController.validation), + RemoveController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/guilds/list.controller.ts b/apps/api/src/app/controllers/pools/guilds/list.controller.ts new file mode 100644 index 000000000..3beb0e2b9 --- /dev/null +++ b/apps/api/src/app/controllers/pools/guilds/list.controller.ts @@ -0,0 +1,13 @@ +import PoolService from '@thxnetwork/api/services/PoolService'; +import { Request, Response } from 'express'; +import { param } from 'express-validator'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + const guilds = await PoolService.findGuilds(pool); + res.json(guilds); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/guilds/patch.controller.ts b/apps/api/src/app/controllers/pools/guilds/patch.controller.ts new file mode 100644 index 000000000..26f9f5a50 --- /dev/null +++ b/apps/api/src/app/controllers/pools/guilds/patch.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { DiscordGuild } from '@thxnetwork/api/models'; +import DiscordDataProxy from '@thxnetwork/api/proxies/DiscordDataProxy'; +import * as CreateController from './post.controller'; + +const validation = [param('guildId').optional().isMongoId(), ...CreateController.validation]; + +const controller = async (req: Request, res: Response) => { + const { secret, adminRoleId, channelId } = req.body; + const guild = await DiscordGuild.findByIdAndUpdate( + req.params.guildId, + { secret, channelId, adminRoleId, poolId: req.params.id }, + { new: true }, + ); + const result = await DiscordDataProxy.getGuild({ ...guild.toJSON(), isConnected: true }); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/guilds/post.controller.ts b/apps/api/src/app/controllers/pools/guilds/post.controller.ts new file mode 100644 index 000000000..d3137a6db --- /dev/null +++ b/apps/api/src/app/controllers/pools/guilds/post.controller.ts @@ -0,0 +1,27 @@ +import { DiscordGuild } from '@thxnetwork/api/models'; +import DiscordDataProxy from '@thxnetwork/api/proxies/DiscordDataProxy'; +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; + +const validation = [ + param('id').isMongoId(), + body('settings.channelId').optional().isString(), + body('settings.adminRoleId').optional().isString(), +]; + +const controller = async (req: Request, res: Response) => { + const { guildId, name, adminRoleId, channelId } = req.body; + const guild = await DiscordGuild.create({ + sub: req.auth.sub, + guildId, + name, + channelId, + adminRoleId, + poolId: req.params.id, + }); + const result = await DiscordDataProxy.getGuild({ ...guild.toJSON(), isConnected: true }); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/identities/delete.controller.ts b/apps/api/src/app/controllers/pools/identities/delete.controller.ts new file mode 100644 index 000000000..43b11cad6 --- /dev/null +++ b/apps/api/src/app/controllers/pools/identities/delete.controller.ts @@ -0,0 +1,17 @@ +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Identity } from '@thxnetwork/api/models/Identity'; +import { param } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find pool for client'); + + await Identity.findByIdAndDelete(req.params.identityId); + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/identities/get.controller.ts b/apps/api/src/app/controllers/pools/identities/get.controller.ts new file mode 100644 index 000000000..85e3ec6e1 --- /dev/null +++ b/apps/api/src/app/controllers/pools/identities/get.controller.ts @@ -0,0 +1,19 @@ +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { param, query } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [param('id').isMongoId(), query('page').isInt(), query('limit').isInt()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find pool for client'); + + const page = Number(req.query.page); + const limit = Number(req.query.limit); + const identities = await PoolService.findIdentities(pool, page, limit); + + res.json(identities); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/identities/identities.router.ts b/apps/api/src/app/controllers/pools/identities/identities.router.ts new file mode 100644 index 000000000..0603f6a22 --- /dev/null +++ b/apps/api/src/app/controllers/pools/identities/identities.router.ts @@ -0,0 +1,23 @@ +import express from 'express'; +import * as CreateController from './post.controller'; +import * as ListController from './get.controller'; +import * as DeleteController from './delete.controller'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', guard.check(['pools:read']), assertRequestInput(ListController.validation), ListController.controller); +router.post( + '/', + guard.check(['pools:read', 'pools:write']), + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.delete( + '/:identityId', + guard.check(['pools:read', 'pools:write']), + assertRequestInput(DeleteController.validation), + DeleteController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/identities/post.controller.ts b/apps/api/src/app/controllers/pools/identities/post.controller.ts new file mode 100644 index 000000000..5f85c0308 --- /dev/null +++ b/apps/api/src/app/controllers/pools/identities/post.controller.ts @@ -0,0 +1,20 @@ +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Identity } from '@thxnetwork/api/models/Identity'; +import { uuidV1 } from '@thxnetwork/api/util/uuid'; +import { param } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find pool for client'); + + const uuid = uuidV1(); + const id = await Identity.create({ poolId: pool._id, uuid }); + + res.json(id.uuid); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/integrations/integrations.router.ts b/apps/api/src/app/controllers/pools/integrations/integrations.router.ts new file mode 100644 index 000000000..886f3fc84 --- /dev/null +++ b/apps/api/src/app/controllers/pools/integrations/integrations.router.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import RouterTwitter from './twitter/twitter.router'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.use('/twitter', RouterTwitter); + +export default router; diff --git a/apps/api/src/app/controllers/pools/integrations/twitter/queries/delete.controller.ts b/apps/api/src/app/controllers/pools/integrations/twitter/queries/delete.controller.ts new file mode 100644 index 000000000..f1a5284c3 --- /dev/null +++ b/apps/api/src/app/controllers/pools/integrations/twitter/queries/delete.controller.ts @@ -0,0 +1,17 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { TwitterQuery } from '@thxnetwork/api/models'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId(), param('queryId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const query = await TwitterQuery.findById(req.params.queryId); + if (query.poolId !== req.params.id) throw new ForbiddenError('Not your quest.'); + + await TwitterQuery.findByIdAndDelete(req.params.queryId); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/integrations/twitter/queries/list.controller.ts b/apps/api/src/app/controllers/pools/integrations/twitter/queries/list.controller.ts new file mode 100644 index 000000000..368485254 --- /dev/null +++ b/apps/api/src/app/controllers/pools/integrations/twitter/queries/list.controller.ts @@ -0,0 +1,13 @@ +import { Request, Response } from 'express'; +import { TwitterQuery } from '@thxnetwork/api/models'; +import { param } from 'express-validator'; +import TwitterQueryService from '@thxnetwork/api/services/TwitterQueryService'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const queries = await TwitterQueryService.list({ poolId: req.params.id }); + res.json(queries); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/integrations/twitter/queries/patch.controller.ts b/apps/api/src/app/controllers/pools/integrations/twitter/queries/patch.controller.ts new file mode 100644 index 000000000..89addf2fe --- /dev/null +++ b/apps/api/src/app/controllers/pools/integrations/twitter/queries/patch.controller.ts @@ -0,0 +1,22 @@ +import { body, param } from 'express-validator'; +import { Request, Response } from 'express'; +import { TwitterQuery } from '@thxnetwork/api/models'; +import { TwitterQuery as TwitterQueryParser } from '@thxnetwork/common/twitter'; + +const validation = [ + param('id').isMongoId(), + param('queryId').isMongoId(), + body('operators').customSanitizer((ops) => TwitterQueryParser.parse(ops)), +]; + +const controller = async (req: Request, res: Response) => { + const query = TwitterQueryParser.create(req.body.operators); + const twitterQuery = await TwitterQuery.findByIdAndUpdate(req.params.queryId, { + operators: req.body.operators, + query, + }); + + res.json(twitterQuery); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/integrations/twitter/queries/post.controller.ts b/apps/api/src/app/controllers/pools/integrations/twitter/queries/post.controller.ts new file mode 100644 index 000000000..23dae01e8 --- /dev/null +++ b/apps/api/src/app/controllers/pools/integrations/twitter/queries/post.controller.ts @@ -0,0 +1,31 @@ +import { body, param } from 'express-validator'; +import { Request, Response } from 'express'; +import { TwitterQuery } from '@thxnetwork/api/models'; +import { TwitterQuery as TwitterQueryParser } from '@thxnetwork/common/twitter'; +import { BadRequestError } from '@thxnetwork/api/util/errors'; +import TwitterQueryService from '@thxnetwork/api/services/TwitterQueryService'; + +const validation = [param('id').isMongoId(), body('operators').customSanitizer((ops) => TwitterQueryParser.parse(ops))]; + +const controller = async (req: Request, res: Response) => { + const query = TwitterQueryParser.create(req.body.operators); + + // 512 is the max length for X API queries within the Basic plan + if (query.length > 512) { + throw new BadRequestError('Your query is too long! Please remove some fields.'); + } + + const twitterQuery = await TwitterQuery.create({ + poolId: req.params.id, + operators: req.body.operators, + defaults: req.body.defaults, + query, + }); + + // Search initial posts and create quests + await TwitterQueryService.run([twitterQuery]); + + res.status(201).json(twitterQuery); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/integrations/twitter/twitter.router.ts b/apps/api/src/app/controllers/pools/integrations/twitter/twitter.router.ts new file mode 100644 index 000000000..ca75ffc7d --- /dev/null +++ b/apps/api/src/app/controllers/pools/integrations/twitter/twitter.router.ts @@ -0,0 +1,39 @@ +import express from 'express'; +import * as CreateController from './queries/post.controller'; +import * as UpdateController from './queries/patch.controller'; +import * as DeleteController from './queries/delete.controller'; +import * as ListController from './queries/list.controller'; +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/queries', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListController.validation), + ListController.controller, +); +router.post( + '/queries', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.patch( + '/queries/queryId', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/queries/:queryId', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(DeleteController.validation), + DeleteController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/invoices/invoices.router.ts b/apps/api/src/app/controllers/pools/invoices/invoices.router.ts new file mode 100644 index 000000000..0f67df758 --- /dev/null +++ b/apps/api/src/app/controllers/pools/invoices/invoices.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import * as ListInvoices from './list.controller'; +import { assertRequestInput, guard } from '@thxnetwork/api/middlewares'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', guard.check(['pools:read']), assertRequestInput(ListInvoices.validation), ListInvoices.controller); + +export default router; diff --git a/apps/api/src/app/controllers/pools/invoices/list.controller.ts b/apps/api/src/app/controllers/pools/invoices/list.controller.ts new file mode 100644 index 000000000..57e6a68b8 --- /dev/null +++ b/apps/api/src/app/controllers/pools/invoices/list.controller.ts @@ -0,0 +1,15 @@ +import { Request, Response } from 'express'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { Invoice } from '@thxnetwork/api/models'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new Error('Pool not found'); + + const invoices = await Invoice.find({ poolId: pool._id }); + res.json(invoices); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/list.controller.ts b/apps/api/src/app/controllers/pools/list.controller.ts new file mode 100644 index 000000000..de5486a88 --- /dev/null +++ b/apps/api/src/app/controllers/pools/list.controller.ts @@ -0,0 +1,11 @@ +import { Request, Response } from 'express'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + const pools = await PoolService.getAllBySub(req.auth.sub); + res.json(pools); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/participants/list.controller.ts b/apps/api/src/app/controllers/pools/participants/list.controller.ts new file mode 100644 index 000000000..3190e4a54 --- /dev/null +++ b/apps/api/src/app/controllers/pools/participants/list.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [ + param('id').isMongoId(), + query('page').isInt(), + query('limit').isInt(), + query('page').optional().isString(), + query('query').optional().isString().isLength({ min: 3 }), +]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + const { page, limit, query } = req.query; + const participants = await PoolService.findParticipants(pool, Number(page), Number(limit), query as string); + + res.json(participants); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/participants/participants.router.ts b/apps/api/src/app/controllers/pools/participants/participants.router.ts new file mode 100644 index 000000000..69b72b83b --- /dev/null +++ b/apps/api/src/app/controllers/pools/participants/participants.router.ts @@ -0,0 +1,23 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ListParticipants from './list.controller'; +import * as UpdateParticipants from './patch.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListParticipants.validation), + ListParticipants.controller, +); +router.patch( + '/:participantId', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(UpdateParticipants.validation), + UpdateParticipants.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/participants/patch.controller.ts b/apps/api/src/app/controllers/pools/participants/patch.controller.ts new file mode 100644 index 000000000..9bc97b22d --- /dev/null +++ b/apps/api/src/app/controllers/pools/participants/patch.controller.ts @@ -0,0 +1,29 @@ +import { Participant } from '@thxnetwork/api/models/Participant'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; + +const validation = [ + param('id').isMongoId(), + param('participantId').isMongoId(), + body('pointBalance').optional().isInt({ min: 0 }), +]; + +const controller = async (req: Request, res: Response) => { + const participant = await Participant.findById(req.params.participantId); + if (!participant) throw new NotFoundError('Participant not found.'); + + let pointBalance; + if (typeof req.body.pointBalance !== 'undefined') { + const { balance } = await Participant.findOneAndUpdate( + { poolId: participant.poolId, sub: participant.sub }, + { balance: Number(req.body.pointBalance) }, + { new: true, upsert: true }, + ); + pointBalance = balance; + } + + res.json({ ...participant.toJSON(), pointBalance }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/patch.controller.ts b/apps/api/src/app/controllers/pools/patch.controller.ts new file mode 100644 index 000000000..918c1510d --- /dev/null +++ b/apps/api/src/app/controllers/pools/patch.controller.ts @@ -0,0 +1,54 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { BadRequestError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { Pool } from '@thxnetwork/api/models'; +import { JobType, agenda } from '@thxnetwork/api/util/agenda'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [ + param('id').exists(), + body('settings.title').optional().isString().trim().escape().isLength({ max: 50 }), + body('settings.slug').optional().isString().trim().escape().isLength({ min: 3, max: 25 }), + body('settings.description').optional().isString().trim().escape().isLength({ max: 255 }), + body('settings.startDate').optional({ nullable: true }).isString(), + body('settings.endDate').optional({ nullable: true }).isString(), + body('settings.discordWebhookUrl').optional({ checkFalsy: true }).isURL(), + body('settings.isArchived').optional().isBoolean(), + body('settings.isPublished').optional().isBoolean(), + body('settings.isWeeklyDigestEnabled').optional().isBoolean(), + body('settings.isTwitterSyncEnabled').optional().isBoolean(), + body('settings.defaults.conditionalRewards.title').optional().isString(), + body('settings.defaults.conditionalRewards.description').optional().isString(), + body('settings.defaults.conditionalRewards.amount').optional().isInt(), + body('settings.defaults.conditionalRewards.hashtag').optional().isString(), + body('settings.defaults.conditionalRewards.isPublished').optional().isBoolean(), + body('settings.authenticationMethods').optional().isArray(), +]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Pools'] + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find the Asset Pool for this id'); + + const { settings } = req.body; + const isSlugUsed = !!(await Pool.exists({ + '_id': { $ne: pool._id }, + 'settings.slug': settings.slug, + })); + if (settings && settings.slug && isSlugUsed) { + throw new BadRequestError('This slug is in use already.'); + } + + const result = await Pool.findByIdAndUpdate( + pool._id, + { settings: Object.assign(pool.settings, req.body.settings) }, + { new: true }, + ); + + if (settings.isPublished && settings.isPublished !== pool.settings.isPublished) { + await agenda.now(JobType.UpdateCampaignRanks); + } + + return res.json(result); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/payments/payments.router.ts b/apps/api/src/app/controllers/pools/payments/payments.router.ts new file mode 100644 index 000000000..143b2c945 --- /dev/null +++ b/apps/api/src/app/controllers/pools/payments/payments.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as CreatePayments from './post.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post( + '/', + guard.check(['pools:read', 'pools:write']), + assertPoolAccess, + assertRequestInput(CreatePayments.validation), + CreatePayments.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/payments/post.controller.ts b/apps/api/src/app/controllers/pools/payments/post.controller.ts new file mode 100644 index 000000000..8bd893246 --- /dev/null +++ b/apps/api/src/app/controllers/pools/payments/post.controller.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; +import { InsufficientAllowanceError, InsufficientBalanceError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { body, param } from 'express-validator'; +import { BigNumber } from 'alchemy-sdk'; +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { getProvider } from '@thxnetwork/api/util/network'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import PaymentService from '@thxnetwork/api/services/PaymentService'; + +const validation = [param('id').isMongoId(), body('amountInWei').exists(), body('planType').isInt()]; + +// TODO +// 1. Customer approves USDC for Campaign Safe for x allowance +// 2. Campaign Safe calls multiSend for multiple transactions +// 2.1 transfer 30% of USDC allowance to Company Safe +// 2.2 joinPool 70% of USDC allowance to BalancerVault +// 2.3 stake 100% of BPT +// 2.4 transfer 75% of BPTGauge to RewardDistributor +// 3. hold 25% of BPTGauge for Quest Incentives (or autocompounding) + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find campaign'); + + const safe = await SafeService.findOneByPool(pool, pool.chainId); + if (!safe) throw new NotFoundError('Could not find campaign Safe'); + + const amountInWei = BigNumber.from(req.body.amountInWei); + const addresses = contractNetworks[safe.chainId]; + + // Assert USDC balance for Safe to ensure throughput + const { web3 } = getProvider(safe.chainId); + const usdc = new web3.eth.Contract(contractArtifacts['USDC'].abi, addresses.USDC); + const balance = await usdc.methods.balanceOf(safe.address).call(); + if (BigNumber.from(balance).lt(amountInWei)) { + throw new InsufficientBalanceError(); + } + + // Assert allowance for Safe to PaymentSplitter + const allowance = await usdc.methods.allowance(safe.address, addresses.THXPaymentSplitter).call(); + if (BigNumber.from(allowance).lt(amountInWei)) { + throw new InsufficientAllowanceError(); + } + + // Execute approve from Safe to PaymentSplitter + await PaymentService.deposit(safe, req.auth.sub, amountInWei); + + res.status(201).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/pools.router.ts b/apps/api/src/app/controllers/pools/pools.router.ts new file mode 100644 index 000000000..6774c92fe --- /dev/null +++ b/apps/api/src/app/controllers/pools/pools.router.ts @@ -0,0 +1,77 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, assertPayment, guard } from '@thxnetwork/api/middlewares'; + +import * as ListController from './list.controller'; +import * as ReadController from './get.controller'; +import * as CreateController from './post.controller'; +import * as UpdateController from './patch.controller'; +import * as DeleteController from './delete.controller'; + +import RouterCollaborators from './collaborators/collaborators.router'; +import RouterParticipants from './participants/participants.router'; +import RouterAnalytics from './analytics/analytics.router'; +import RouterEvents from './events/events.router'; +import RouterQuests from './quests/quests.router'; +import RouterRewards from './rewards/rewards.router'; +import RouterGuilds from './guilds/guilds.router'; +import RouterPayments from './payments/payments.router'; +import RouterWallets from './wallets/wallets.router'; +import RouterERC20 from './erc20/erc20.router'; +import RouterER1155 from './erc1155/erc1155.router'; +import RouterIdentities from './identities/identities.router'; +import RouterInvoices from './invoices/invoices.router'; +import RouterIntegrations from './integrations/integrations.router'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', guard.check(['pools:read']), assertRequestInput(ListController.validation), ListController.controller); +router.post( + '/', + guard.check(['pools:read', 'pools:write']), + assertRequestInput(CreateController.validation), + CreateController.controller, +); + +// This route is also asserted for payment but not for access +router.use('/:id/collaborators', assertPayment, RouterCollaborators); + +// Everything below is asserted for campaign/pool access +router.use('/:id', assertPoolAccess); +router.get( + '/:id', + guard.check(['pools:read']), + assertRequestInput(ReadController.validation), + ReadController.controller, +); +router.patch( + '/:id', + guard.check(['pools:read', 'pools:write']), + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/:id', + guard.check(['pools:write']), + assertRequestInput(DeleteController.validation), + DeleteController.controller, +); + +// Payment related routes that require access event if payment assertion fails +router.use('/:id/erc20', RouterERC20); // Needed for payment processing +router.use('/:id/payments', RouterPayments); +router.use('/:id/invoices', RouterInvoices); + +// Everything below is asserted for payment +router.use('/:id', assertPayment); +router.use('/:id/analytics', RouterAnalytics); +router.use('/:id/quests', RouterQuests); +router.use('/:id/rewards', RouterRewards); +router.use('/:id/participants', RouterParticipants); +router.use('/:id/wallets', RouterWallets); +router.use('/:id/events', RouterEvents); +router.use('/:id/guilds', RouterGuilds); +router.use('/:id/erc1155', RouterER1155); +router.use('/:id/identities', RouterIdentities); +router.use('/:id/integrations', RouterIntegrations); + +export default router; diff --git a/apps/api/src/app/controllers/pools/pools.test.ts b/apps/api/src/app/controllers/pools/pools.test.ts new file mode 100644 index 000000000..ae6bbd964 --- /dev/null +++ b/apps/api/src/app/controllers/pools/pools.test.ts @@ -0,0 +1,88 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { isAddress } from 'web3-utils'; +import { timeTravel } from '@thxnetwork/api/util/jest/network'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { poll } from '@thxnetwork/api/util/polling'; + +const user = request.agent(app); + +describe('Default Pool', () => { + let poolId: string, safe: { address: string }; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + describe('POST /pools', () => { + it('HTTP 201 (success)', async () => { + const { body, status } = await user + .post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send({ title: 'My Pool', chainId: ChainId.Hardhat }); + expect(status).toBe(201); + poolId = body._id; + expect(body.safe.address).toBeDefined(); + safe = body.safe; + expect(body.settings.title).toBe('My Pool'); + }); + + it('HTTP 200 (multisig deployed)', async () => { + // Wait for campaign safe to be deployed + const { web3 } = getProvider(ChainId.Hardhat); + await poll( + () => web3.eth.getCode(safe.address), + (data: string) => data === '0x', + 1000, + ); + + await user + .get(`/v1/pools/${poolId}`) + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .expect((res: request.Response) => { + expect(isAddress(res.body.safeAddress)).toBe(true); + }) + .expect(200); + }); + }); + + // describe('GET /pools/:id (post trial)', () => { + // it('HTTP 403 after 2 weeks', async () => { + // // Skip 2 weeks + // await timeTravel(60 * 60 * 24 * 14); + + // await user + // .get('/v1/pools/' + poolId) + // .set({ Authorization: dashboardAccessToken }) + // .expect(403); + // }); + // }); + + describe('PATCH /pools/:id', () => { + it('HTTP 200', (done) => { + user.patch('/v1/pools/' + poolId) + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .send({ + settings: { + title: 'My Pool 2', + isArchived: true, + }, + }) + .expect(({ body }: request.Response) => { + expect(body.settings.title).toBe('My Pool 2'); + expect(body.settings.isArchived).toBe(true); + }) + .expect(200, done); + }); + }); + + describe('DELETE /pools/:id', () => { + it('HTTP 204', (done) => { + user.delete('/v1/pools/' + poolId) + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .expect(204, done); + }); + }); +}); diff --git a/apps/api/src/app/controllers/pools/post.controller.ts b/apps/api/src/app/controllers/pools/post.controller.ts new file mode 100644 index 000000000..1a9f6ae68 --- /dev/null +++ b/apps/api/src/app/controllers/pools/post.controller.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import ContractService, { safeVersion } from '@thxnetwork/api/services/ContractService'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const validation = [body('settings.title').optional().isString().trim().escape().isLength({ max: 50 })]; + +const controller = async (req: Request, res: Response) => { + const { title } = req.body; + const pool = await PoolService.deploy(req.auth.sub, title || 'My Quest Campaign'); + + // Deploy a Safe for the campaign + const poolId = String(pool._id); + const chainId = ContractService.getChainId(); + const safe = await SafeService.create({ chainId, sub: req.auth.sub, safeVersion, poolId }); + + // Update predicted safe address for pool + await pool.updateOne({ safeAddress: safe.address }); + + res.status(201).json({ ...pool.toJSON(), safeAddress: safe.address, safe }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/quests/delete.controller.ts b/apps/api/src/app/controllers/pools/quests/delete.controller.ts new file mode 100644 index 000000000..de864badd --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/delete.controller.ts @@ -0,0 +1,26 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import LockService from '@thxnetwork/api/services/LockService'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const validation = [param('id').isMongoId(), param('variant').isInt(), param('questId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const variant = req.params.variant as unknown as QuestVariant; + const poolId = req.params.id; + const questId = req.params.questId; + + const quest = await QuestService.findById(variant, questId); + if (quest.poolId !== poolId) throw new ForbiddenError('Not your quest.'); + + await quest.deleteOne(); + + // Remove all locks for this quest + await LockService.removeAllLocks(questId); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/quests/entries/entries.router.ts b/apps/api/src/app/controllers/pools/quests/entries/entries.router.ts new file mode 100644 index 000000000..3d16d3285 --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/entries/entries.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ListController from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListController.validation), + ListController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/quests/entries/list.controller.ts b/apps/api/src/app/controllers/pools/quests/entries/list.controller.ts new file mode 100644 index 000000000..fd731be20 --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/entries/list.controller.ts @@ -0,0 +1,26 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const validation = [ + param('id').isMongoId(), + param('variant').isString(), + param('questId').isMongoId(), + query('page').isInt(), + query('limit').isInt(), +]; + +const controller = async (req: Request, res: Response) => { + const variant = req.params.variant as unknown as QuestVariant; + const questId = req.params.questId as string; + const quest = await QuestService.findById(variant, questId); + const entries = await QuestService.findEntries(quest, { + page: Number(req.query.page), + limit: Number(req.query.limit), + }); + + res.json(entries); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/quests/list.controller.ts b/apps/api/src/app/controllers/pools/quests/list.controller.ts new file mode 100644 index 000000000..0bd892333 --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/list.controller.ts @@ -0,0 +1,60 @@ +import { param, query } from 'express-validator'; +import { Request, Response } from 'express'; +import { + QuestInvite, + QuestSocial, + QuestCustom, + QuestWeb3, + QuestGitcoin, + QuestDaily, + QuestWebhook, +} from '@thxnetwork/api/models'; + +const validation = [ + param('id').isMongoId(), + query('page').isInt(), + query('limit').isInt(), + query('isPublished') + .optional() + .isBoolean() + .customSanitizer((value) => { + return value && JSON.parse(value); + }), +]; + +const controller = async (req: Request, res: Response) => { + const poolId = req.params.id; + const page = Number(req.query.page); + const limit = Number(req.query.limit); + const $match = { poolId, isPublished: req.query.isPublished }; + const pipeline = [ + { $unionWith: { coll: QuestInvite.collection.name } }, + { $unionWith: { coll: QuestSocial.collection.name } }, + { $unionWith: { coll: QuestCustom.collection.name } }, + { $unionWith: { coll: QuestWeb3.collection.name } }, + { $unionWith: { coll: QuestGitcoin.collection.name } }, + { $unionWith: { coll: QuestWebhook.collection.name } }, + { $match }, + ]; + const arr = await Promise.all( + [QuestDaily, QuestInvite, QuestSocial, QuestCustom, QuestWeb3, QuestGitcoin, QuestWebhook].map( + async (model) => await model.countDocuments($match), + ), + ); + const total = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + const results = await QuestDaily.aggregate([ + ...pipeline, + { $sort: { index: 1 } }, + { $skip: (page - 1) * limit }, + { $limit: limit }, + ]); + + res.json({ + total, + limit, + page, + results, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/quests/patch.controller.ts b/apps/api/src/app/controllers/pools/quests/patch.controller.ts new file mode 100644 index 000000000..7a8af5128 --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/patch.controller.ts @@ -0,0 +1,19 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import * as CreateController from '@thxnetwork/api/controllers/pools/quests/post.controller'; + +const validation = [param('variant').isInt(), param('questId').isMongoId(), ...CreateController.validation]; + +const controller = async (req: Request, res: Response) => { + const variant = req.params.variant as unknown as QuestVariant; + const questId = req.params.questId as string; + + let quest = await QuestService.findById(variant, questId); + quest = await QuestService.update(quest, req.body, req.file); + + res.json(quest); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/quests/post.controller.ts b/apps/api/src/app/controllers/pools/quests/post.controller.ts new file mode 100644 index 000000000..1b5457657 --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/post.controller.ts @@ -0,0 +1,59 @@ +import { body, param } from 'express-validator'; +import { Request, Response } from 'express'; +import { isValidUrl } from '@thxnetwork/api/util/url'; +import { ChainId } from '@thxnetwork/common/enums'; +import { isAddress } from 'web3-utils'; +import { defaults } from '@thxnetwork/api/util/validation'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const validationBaseQuest = [ + param('id').isMongoId(), + ...defaults.quest, + // Daily + body('amounts') + .optional() + .custom((amounts) => { + for (const amount of JSON.parse(amounts)) { + if (isNaN(amount)) return false; + } + return true; + }) + .customSanitizer((amounts) => JSON.parse(amounts)), + body('eventName').optional().isString(), + // Invite + body('successUrl') + .optional() + .custom((value) => { + if (value === '' || isValidUrl(value)) return true; + return false; + }), + body('isMandatoryReview').optional().isBoolean(), + // Social + body('kind').optional().isString(), + body('interaction').optional().isNumeric(), + body('content').optional().isString(), + body('contentMetadata').optional().isString(), + // Custom + body('limit').optional().isInt(), + // Web3 + body('contracts') + .optional() + .customSanitizer((contracts) => { + return JSON.parse(contracts).filter((contract: { address: string; chainId: ChainId }) => + isAddress(contract.address), + ); + }), + body('methodName').optional().isString(), + body('threshold').optional().isString(), + // Gitcoin + // +]; + +const validation = [param('id').isMongoId(), ...validationBaseQuest]; + +const controller = async (req: Request, res: Response) => { + const quest = await QuestService.create(req.body.variant, req.params.id, req.body, req.file); + res.status(201).json(quest); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/quests/quests.router.ts b/apps/api/src/app/controllers/pools/quests/quests.router.ts new file mode 100644 index 000000000..522451224 --- /dev/null +++ b/apps/api/src/app/controllers/pools/quests/quests.router.ts @@ -0,0 +1,49 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import { upload } from '@thxnetwork/api/util/multer'; + +import * as ListController from './list.controller'; +import * as CreateController from './post.controller'; +import * as UpdateController from './patch.controller'; +import * as RemoveController from './delete.controller'; + +import RouterQuestEntries from './entries/entries.router'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(ListController.validation), + ListController.controller, +); +router.post( + '/:variant', + guard.check(['pools:read', 'pools:write']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.patch( + '/:variant/:questId', + guard.check(['pools:read', 'pools:write']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/:variant/:questId', + guard.check(['pools:read', 'pools:write']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(RemoveController.validation), + RemoveController.controller, +); + +router.use('/:variant/:questId/entries', RouterQuestEntries); + +export default router; diff --git a/apps/api/src/app/controllers/pools/rewards/coin-rewards.test.ts b/apps/api/src/app/controllers/pools/rewards/coin-rewards.test.ts new file mode 100644 index 000000000..fc58e0d73 --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/coin-rewards.test.ts @@ -0,0 +1,110 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId, ERC20Type, RewardVariant } from '@thxnetwork/common/enums'; +import { dashboardAccessToken, tokenName, tokenSymbol } from '@thxnetwork/api/util/jest/constants'; +import { isAddress } from 'web3-utils'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { addMinutes } from '@thxnetwork/api/util/date'; +import { createImage } from '@thxnetwork/api/util/jest/images'; +import { ERC20Document } from '@thxnetwork/api/models/ERC20'; +import { RewardCoinDocument } from '@thxnetwork/api/models/RewardCoin'; + +const user = request.agent(app); + +describe('Coin Rewards', () => { + let poolId: string, erc20: ERC20Document, reward: RewardCoinDocument; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + it('POST /erc20', (done) => { + user.post('/v1/erc20') + .set('Authorization', dashboardAccessToken) + .send({ + chainId: ChainId.Hardhat, + name: tokenName, + symbol: tokenSymbol, + type: ERC20Type.Unlimited, + totalSupply: 0, + }) + .expect(({ body }: request.Response) => { + erc20 = body; + expect(isAddress(body.address)).toBe(true); + }) + .expect(201, done); + }); + + it('POST /pools', (done) => { + user.post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send({ + chainId: ChainId.Hardhat, + }) + .expect((res: request.Response) => { + expect(isAddress(res.body.safeAddress)).toBe(true); + poolId = res.body._id; + }) + .expect(201, done); + }); + + it('POST /pools/:poolId/rewards/:variant', (done) => { + const title = 'Lorem', + description = 'Ipsum', + expiryDate = addMinutes(new Date(), 30), + pointPrice = 200, + image = createImage(), + amount = '1', + limit = 0, + isPromoted = true, + isPublished = true; + user.post(`/v1/pools/${poolId}/rewards/${RewardVariant.Coin}`) + .set({ Authorization: dashboardAccessToken }) + .attach('file', image, { + filename: 'test.jpg', + contentType: 'image/jpg', + }) + .field({ + title, + description, + image, + limit, + pointPrice, + expiryDate: new Date(expiryDate).toISOString(), + amount, + erc20Id: String(erc20._id), + isPromoted, + isPublished, + }) + .expect((res: request.Response) => { + expect(res.body.uuid).toBeDefined(); + expect(res.body.title).toBe(title); + expect(res.body.description).toBe(description); + expect(res.body.image).toBeDefined(); + expect(res.body.amount).toBe(amount); + expect(res.body.pointPrice).toBe(pointPrice); + expect(new Date(res.body.expiryDate).getDate()).toBe(expiryDate.getDate()); + expect(res.body.limit).toBe(limit); + expect(res.body.isPromoted).toBe(true); + }) + .expect(201, done); + }); + + it('GET /pools/:poolId/rewards', (done) => { + user.get(`/v1/pools/${poolId}/rewards`) + .set({ Authorization: dashboardAccessToken }) + .query({ page: 1, limit: 10, isPublished: true }) + .expect((res: request.Response) => { + expect(res.body.results.length).toBe(1); + expect(res.body.limit).toBe(10); + expect(res.body.total).toBe(1); + reward = res.body.results[0]; + }) + .expect(200, done); + }); + + it('DELETE /pools/:poolId/rewards/:variant', (done) => { + user.delete(`/v1/pools/${poolId}/rewards/${reward.variant}/${reward._id}`) + .set({ Authorization: dashboardAccessToken }) + .expect(204, done); + }); +}); diff --git a/apps/api/src/app/controllers/pools/rewards/delete.controller.ts b/apps/api/src/app/controllers/pools/rewards/delete.controller.ts new file mode 100644 index 000000000..9c04fa72c --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/delete.controller.ts @@ -0,0 +1,26 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { QRCodeEntry } from '@thxnetwork/api/models/QRCodeEntry'; +import RewardService from '@thxnetwork/api/services/RewardService'; + +const validation = [param('id').isMongoId(), param('variant').isInt(), param('rewardId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const poolId = req.params.id; + const variant = req.params.variant as unknown as RewardVariant; + const rewardId = req.params.rewardId; + + const reward = await RewardService.findById(variant, rewardId); + if (reward.poolId !== poolId) throw new ForbiddenError('Not your reward.'); + + await reward.deleteOne(); + + // Delete QR codes for this reward if any + await QRCodeEntry.deleteMany({ rewardId }); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/rewards/discord-role-rewards.router.ts b/apps/api/src/app/controllers/pools/rewards/discord-role-rewards.router.ts new file mode 100644 index 000000000..9fc008b2d --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/discord-role-rewards.router.ts @@ -0,0 +1,46 @@ +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import { upload } from '@thxnetwork/api/util/multer'; +import express from 'express'; +import * as ListDiscordRoleReward from './list.controller'; +import * as ListCouponCodePayments from './payments/list.controller'; +import * as CreateDiscordRoleReward from './post.controller'; +import * as UpdateDiscordRoleReward from './patch.controller'; +import * as RemoveDiscordRoleReward from './delete.controller'; + +const router: express.Router = express.Router(); + +router.get( + '/', + guard.check(['discord_role_rewards:read']), + assertPoolAccess, + assertRequestInput(ListDiscordRoleReward.validation), + ListDiscordRoleReward.controller, +); + +router.get('/payments', ListCouponCodePayments.controller); + +router.patch( + '/:id', + upload.single('file'), + guard.check(['discord_role_rewards:write', 'discord_role_rewards:read']), + assertPoolAccess, + assertRequestInput(UpdateDiscordRoleReward.validation), + UpdateDiscordRoleReward.controller, +); +router.post( + '/', + upload.single('file'), + guard.check(['discord_role_rewards:write', 'discord_role_rewards:read']), + assertPoolAccess, + assertRequestInput(CreateDiscordRoleReward.validation), + CreateDiscordRoleReward.controller, +); +router.delete( + '/:id', + guard.check(['discord_role_rewards:write']), + assertPoolAccess, + assertRequestInput(RemoveDiscordRoleReward.validation), + RemoveDiscordRoleReward.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/rewards/list.controller.ts b/apps/api/src/app/controllers/pools/rewards/list.controller.ts new file mode 100644 index 000000000..c55f2dcde --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/list.controller.ts @@ -0,0 +1,70 @@ +import { param, query } from 'express-validator'; +import { Request, Response } from 'express'; +import { + CouponCode, + RewardNFT, + RewardCoupon, + RewardCoin, + RewardDiscordRole, + RewardCustom, + RewardGalachain, +} from '@thxnetwork/api/models'; +import { RewardVariant } from '@thxnetwork/common/enums'; + +const validation = [ + param('id').isMongoId(), + query('page').isInt(), + query('limit').isInt(), + query('isPublished') + .optional() + .isBoolean() + .customSanitizer((value) => { + return value && JSON.parse(value); + }), +]; + +const controller = async (req: Request, res: Response) => { + const poolId = req.params.id; + const page = Number(req.query.page); + const limit = Number(req.query.limit); + const $match = { poolId, isPublished: req.query.isPublished }; + const pipeline = [ + { $unionWith: { coll: RewardNFT.collection.name } }, + { $unionWith: { coll: RewardCoupon.collection.name } }, + { $unionWith: { coll: RewardCustom.collection.name } }, + { $unionWith: { coll: RewardDiscordRole.collection.name } }, + { $unionWith: { coll: RewardGalachain.collection.name } }, + { $match }, + ]; + const arr = await Promise.all( + [RewardCoin, RewardNFT, RewardCoupon, RewardCustom, RewardDiscordRole].map( + async (model) => await model.countDocuments($match), + ), + ); + const total = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + const results = await RewardCoin.aggregate([ + ...pipeline, + { $sort: { index: 1 } }, + { $skip: (page - 1) * limit }, + { $limit: limit }, + ]); + + res.json({ + total, + limit, + page, + results: await Promise.all( + results.map(async (reward) => { + // TODO Move this hack to a service method and make it part of the IRewardService + if (reward.variant === RewardVariant.Coupon) { + const couponCodeCount = await CouponCode.countDocuments({ couponRewardId: reward._id }); + return { ...reward, couponCodeCount }; + } + + return reward; + }), + ), + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/rewards/nft-rewards.test.ts b/apps/api/src/app/controllers/pools/rewards/nft-rewards.test.ts new file mode 100644 index 000000000..0a7a65171 --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/nft-rewards.test.ts @@ -0,0 +1,134 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { isAddress } from 'web3-utils'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { addMinutes } from '@thxnetwork/api/util/date'; +import { createImage } from '@thxnetwork/api/util/jest/images'; +import { RewardNFTDocument } from '@thxnetwork/api/models/RewardNFT'; +import { ERC721Document } from '@thxnetwork/api/models/ERC721'; +import { ERC721MetadataDocument } from '@thxnetwork/api/models/ERC721Metadata'; +import { RewardVariant } from '@thxnetwork/common/enums'; + +const user = request.agent(app); + +describe('NFT Rewards', () => { + let poolId: string, erc721metadata: ERC721MetadataDocument, erc721: ERC721Document, reward: RewardNFTDocument; + const name = 'Planets of the Galaxy', + symbol = 'GLXY', + description = 'description'; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + it('POST /erc721', (done) => { + user.post('/v1/erc721') + .set('Authorization', dashboardAccessToken) + .send({ + chainId: ChainId.Hardhat, + name, + symbol, + description, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(body.address).toBeDefined(); + erc721 = body; + }) + .expect(201, done); + }); + + it('POST /erc721/:id/metadata', (done) => { + const config = { + name: 'Lorem', + description: 'Lorem ipsum dolor sit.', + imageUrl: 'https://image.com', + externalUrl: 'https://example.com', + }; + + user.post('/v1/erc721/' + erc721._id + '/metadata') + .set('Authorization', dashboardAccessToken) + .send(config) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + expect(body.name).toBe(config.name); + expect(body.description).toBe(config.description); + expect(body.image).toBe(config.imageUrl); + expect(body.externalUrl).toBe(config.externalUrl); + erc721metadata = body; + }) + .expect(201, done); + }); + + it('POST /pools', (done) => { + user.post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send({ + chainId: ChainId.Hardhat, + }) + .expect(({ body }: request.Response) => { + expect(isAddress(body.safeAddress)).toBe(true); + poolId = body._id; + }) + .expect(201, done); + }); + + it('POST /pools/:poolId/rewards/:variant', (done) => { + const expiryDate = addMinutes(new Date(), 30); + const image = createImage(); + const config = { + title: 'Lorem', + description: 'Lorem ipsum', + erc721Id: String(erc721._id), + metadataId: erc721metadata._id, + pointPrice: 200, + expiryDate: new Date(expiryDate).toISOString(), + limit: 0, + claimAmount: 0, + isPromoted: true, + variant: RewardVariant.NFT, + isPublished: true, + }; + user.post(`/v1/pools/${poolId}/rewards/${RewardVariant.NFT}`) + .set({ Authorization: dashboardAccessToken }) + .attach('file', image, { + filename: 'test.jpg', + contentType: 'image/jpg', + }) + .field(config) + .expect((res: request.Response) => { + expect(res.body.uuid).toBeDefined(); + expect(res.body.title).toBe(config.title); + expect(res.body.description).toBe(config.description); + expect(res.body.image).toBeDefined(); + expect(res.body.pointPrice).toBe(config.pointPrice); + expect(new Date(res.body.expiryDate).getDate()).toBe(expiryDate.getDate()); + expect(res.body.limit).toBe(config.limit); + expect(res.body.claimAmount).toBe(config.claimAmount); + expect(res.body.isPromoted).toBe(config.isPromoted); + expect(res.body.erc721Id).toBe(erc721._id); + expect(res.body.metadataId).toBe(erc721metadata._id); + }) + .expect(201, done); + }); + + it('GET /pools/:poolId/rewards?page=:page&limit=:limit', (done) => { + user.get(`/v1/pools/${poolId}/rewards`) + .query({ page: 1, limit: 10, isPublished: true }) + .set({ Authorization: dashboardAccessToken }) + .expect((res: request.Response) => { + expect(res.body.results.length).toBe(1); + expect(res.body.limit).toBe(10); + expect(res.body.total).toBe(1); + reward = res.body.results[0]; + }) + .expect(200, done); + }); + + it('DELETE /rewards/:id', (done) => { + user.delete(`/v1/pools/${poolId}/rewards/${reward.variant}/${reward._id}`) + .set({ Authorization: dashboardAccessToken }) + .expect(204, done); + }); +}); diff --git a/apps/api/src/app/controllers/pools/rewards/patch.controller.ts b/apps/api/src/app/controllers/pools/rewards/patch.controller.ts new file mode 100644 index 000000000..f1805678f --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/patch.controller.ts @@ -0,0 +1,19 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import RewardService from '@thxnetwork/api/services/RewardService'; +import * as CreateController from '@thxnetwork/api/controllers/pools/rewards/post.controller'; + +const validation = [param('rewardId').isMongoId(), ...CreateController.validation]; + +const controller = async (req: Request, res: Response) => { + const variant = req.params.variant as unknown as RewardVariant; + const rewardId = req.params.rewardId as string; + + let reward = await RewardService.findById(variant, rewardId); + reward = await RewardService.update(reward, req.body, req.file); + + res.json(reward); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/rewards/payments/list.controller.ts b/apps/api/src/app/controllers/pools/rewards/payments/list.controller.ts new file mode 100644 index 000000000..98a713c9b --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/payments/list.controller.ts @@ -0,0 +1,30 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import RewardService from '@thxnetwork/api/services/RewardService'; + +const validation = [ + param('id').isMongoId(), + param('variant').isString(), + param('rewardId').isMongoId(), + query('page').isInt(), + query('limit').isInt(), + query('query').isString(), +]; + +const controller = async (req: Request, res: Response) => { + const variant = req.params.variant as unknown as RewardVariant; + const reward = await RewardService.findById(variant, req.params.rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + const payments = await RewardService.findPayments(reward, { + page: Number(req.query.page), + limit: Number(req.query.limit), + query: req.query.query as string, + }); + + res.json(payments); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/rewards/payments/payments.router.ts b/apps/api/src/app/controllers/pools/rewards/payments/payments.router.ts new file mode 100644 index 000000000..d280e4317 --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/payments/payments.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ListPayments from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListPayments.validation), + ListPayments.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/pools/rewards/post.controller.ts b/apps/api/src/app/controllers/pools/rewards/post.controller.ts new file mode 100644 index 000000000..aeb30d571 --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/post.controller.ts @@ -0,0 +1,57 @@ +import { body, param } from 'express-validator'; +import { Request, Response } from 'express'; +import { defaults } from '@thxnetwork/api/util/validation'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import RewardService from '@thxnetwork/api/services/RewardService'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validationBaseQuest = [ + param('id').isMongoId(), + ...defaults.reward, + + // Coin + body('erc20Id').optional().isMongoId(), + body('amount').optional().isInt({ gt: 0 }), + + // NFT + body('erc721Id').optional().isString(), + body('erc1155Id').optional().isString(), + body('metadataIds').optional().isString(), + body('tokenId').optional().isString(), + + // Coupon + body('webshopURL').optional().isURL({ require_tld: false }), + body('codes') + .optional() + .custom((value: string) => value && Array.isArray(JSON.parse(value))) + .customSanitizer((value: string) => value && JSON.parse(value)), + + // Custom + body('webhookId').optional().isMongoId(), + body('metadata').optional().isString(), + // DiscordRole + body('discordRoleId').optional().isString(), + // Galachain + body('contractChannelName').optional().isString(), + body('contractChaincodeName').optional().isString(), + body('contractContractName').optional().isString(), + body('tokenCollection').optional().isString(), + body('tokenCategory').optional().isString(), + body('tokenType').optional().isString(), + body('tokenAdditionalKey').optional().isString(), + body('amount').optional().isInt({ gt: 0 }), +]; + +const validation = [param('id').isMongoId(), ...validationBaseQuest]; + +const controller = async (req: Request, res: Response) => { + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Could not find pool'); + const variant = req.params.variant as unknown as RewardVariant; + const reward = await RewardService.create(variant, req.params.id, req.body, req.file); + + res.status(201).json(reward); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/rewards/rewards.router.ts b/apps/api/src/app/controllers/pools/rewards/rewards.router.ts new file mode 100644 index 000000000..c3c5370a2 --- /dev/null +++ b/apps/api/src/app/controllers/pools/rewards/rewards.router.ts @@ -0,0 +1,49 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import { upload } from '@thxnetwork/api/util/multer'; + +import * as ListController from './list.controller'; +import * as UpdateController from './patch.controller'; +import * as CreateController from './post.controller'; +import * as RemoveController from './delete.controller'; + +import RouterRewardPayments from './payments/payments.router'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(ListController.validation), + ListController.controller, +); +router.post( + '/:variant', + guard.check(['pools:read', 'pools:write']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(CreateController.validation), + CreateController.controller, +); +router.patch( + '/:variant/:rewardId', + guard.check(['pools:read', 'pools:write']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(UpdateController.validation), + UpdateController.controller, +); +router.delete( + '/:variant/:rewardId', + guard.check(['pools:read', 'pools:write']), + upload.single('file'), + assertPoolAccess, + assertRequestInput(RemoveController.validation), + RemoveController.controller, +); + +router.use('/:variant/:rewardId/payments', RouterRewardPayments); + +export default router; diff --git a/apps/api/src/app/controllers/pools/wallets/list.controller.ts b/apps/api/src/app/controllers/pools/wallets/list.controller.ts new file mode 100644 index 000000000..3c3b0450a --- /dev/null +++ b/apps/api/src/app/controllers/pools/wallets/list.controller.ts @@ -0,0 +1,12 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Wallet } from '@thxnetwork/api/models'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const wallets = await Wallet.find({ poolId: req.params.id }); + res.json(wallets); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/pools/wallets/wallets.router.ts b/apps/api/src/app/controllers/pools/wallets/wallets.router.ts new file mode 100644 index 000000000..c6cc65c99 --- /dev/null +++ b/apps/api/src/app/controllers/pools/wallets/wallets.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { assertRequestInput, assertPoolAccess, guard } from '@thxnetwork/api/middlewares'; +import * as ListWallets from './list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get( + '/', + guard.check(['pools:read']), + assertPoolAccess, + assertRequestInput(ListWallets.validation), + ListWallets.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/qr-codes/collect/post.controller.ts b/apps/api/src/app/controllers/qr-codes/collect/post.controller.ts new file mode 100644 index 000000000..d5dc5f47e --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/collect/post.controller.ts @@ -0,0 +1,64 @@ +import { Request, Response } from 'express'; +import { param, query } from 'express-validator'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { RewardNFT, RewardNFTPayment, QRCodeEntry, ERC721Metadata } from '@thxnetwork/api/models'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import WalletService from '@thxnetwork/api/services/WalletService'; + +const validation = [param('uuid').isUUID(4), query('walletId').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + let entry = await QRCodeEntry.findOne({ uuid: req.params.uuid }); + if (!entry) throw new BadRequestError('This claim URL is invalid.'); + // Can not be claimed when sub is set for this claim URL and claim amount is greater than 1 + if (entry.sub) throw new ForbiddenError('This NFT is claimed already.'); + + const reward = await RewardNFT.findById(entry.rewardId); + if (!reward) throw new BadRequestError('Reward not found'); + // Can be claimed only if point price is 0 + if (reward.pointPrice > 0) throw new ForbiddenError('Reward needs to be purchased with points.'); + + const pool = await PoolService.getById(reward.poolId); + if (!pool) throw new BadRequestError('Campaign not found.'); + + const safe = await SafeService.findOneByPool(pool, pool.chainId); + if (!safe) throw new BadRequestError('Safe not found.'); + + // Find wallet for the authenticated user + const wallet = await WalletService.findById(req.query.walletId as string); + if (!wallet) throw new NotFoundError('Wallet not found'); + + // Mint an NFT token if the erc721 and metadata for the claim exists. + const metadata = await ERC721Metadata.findById(reward.metadataId); + if (!metadata) throw new NotFoundError('Metadata not found'); + + const erc721 = await ERC721Service.findById(metadata.erc721Id); + if (!erc721) throw new NotFoundError('ERC721 not found'); + + // Mint the NFT + const token = await ERC721Service.mint(safe, erc721, wallet, metadata); + + // Create a payment to register a completed claim. + const payment = await RewardNFTPayment.create({ + sub: req.auth.sub, + rewardId: reward._id, + amount: reward.pointPrice, + poolId: pool._id, + }); + + // Mark claim as claimed by setting sub + entry = await QRCodeEntry.findByIdAndUpdate(entry._id, { sub: req.auth.sub, claimedAt: new Date() }, { new: true }); + + return res.json({ + erc721, + entry, + payment, + token, + metadata, + reward, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/qr-codes/delete.controller.ts b/apps/api/src/app/controllers/qr-codes/delete.controller.ts new file mode 100644 index 000000000..45160b29d --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/delete.controller.ts @@ -0,0 +1,24 @@ +import { QRCodeEntry, RewardNFT } from '@thxnetwork/api/models'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { param } from 'express-validator'; + +const validation = [param('uuid').isUUID(4)]; + +const controller = async (req: Request, res: Response) => { + const entry = await QRCodeEntry.findOne({ uuid: req.params.uuid }); + if (!entry) throw new NotFoundError('QR Code Entry not found'); + + const reward = await RewardNFT.findById(entry.rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + const isAllowed = await PoolService.isSubjectAllowed(req.auth.sub, reward.poolId); + if (!isAllowed) throw new ForbiddenError('Not allowed for delete.'); + + await entry.deleteOne(); + + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/qr-codes/get.controller.ts b/apps/api/src/app/controllers/qr-codes/get.controller.ts new file mode 100644 index 000000000..a753047f9 --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/get.controller.ts @@ -0,0 +1,29 @@ +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { param } from 'express-validator'; +import { QRCodeEntry, RewardNFT, ERC721Metadata } from '@thxnetwork/api/models'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [param('uuid').exists().isUUID(4)]; + +const controller = async (req: Request, res: Response) => { + const entry = await QRCodeEntry.findOne({ uuid: req.params.uuid }); + if (!entry) throw new NotFoundError('QR code entry not found'); + + const reward = await RewardNFT.findById(entry.rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + const pool = await PoolService.getById(reward.poolId); + if (!pool) throw new NotFoundError('Pool not found'); + + const erc721 = await ERC721Service.findById(reward.erc721Id); + if (!erc721) throw new NotFoundError('ERC721 not found'); + + const metadata = await ERC721Metadata.findById(reward.metadataId); + if (!metadata) throw new NotFoundError('Metadata not found'); + + return res.json({ pool, entry, erc721, metadata }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/qr-codes/list.controller.ts b/apps/api/src/app/controllers/qr-codes/list.controller.ts new file mode 100644 index 000000000..083e5f07c --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/list.controller.ts @@ -0,0 +1,44 @@ +import { QRCodeEntry, RewardNFT } from '@thxnetwork/api/models'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import PoolService from '@thxnetwork/api/services/PoolService'; + +const validation = [query('rewardId').isMongoId(), query('page').isInt(), query('limit').isInt()]; + +const controller = async (req: Request, res: Response) => { + const page = Number(req.query.page); + const limit = Number(req.query.limit); + const rewardId = req.query.rewardId; + + const reward = await RewardNFT.findById(rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + const isAllowed = await PoolService.isSubjectAllowed(req.auth.sub, reward.poolId); + if (!isAllowed) throw new ForbiddenError('Reward not accessible.'); + + const total = await QRCodeEntry.countDocuments({ rewardId }); + const entries = await QRCodeEntry.find({ rewardId }) + .limit(limit) + .skip((page - 1) * limit); + const subs = entries.map(({ sub }) => sub); + const accounts = await AccountProxy.find({ subs }); + const results = entries.map((entry) => { + const account = accounts.find((account) => account.sub === entry.sub); + return Object.assign(entry.toJSON(), { account }); + }); + const meta = { + participantCount: await QRCodeEntry.countDocuments({ rewardId, sub: { $exists: true } }), + }; + + res.json({ + total, + limit, + page, + results, + meta, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/qr-codes/post.controller.ts b/apps/api/src/app/controllers/qr-codes/post.controller.ts new file mode 100644 index 000000000..74bb919e0 --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/post.controller.ts @@ -0,0 +1,26 @@ +import { RewardNFT } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import QRCodeService from '@thxnetwork/api/services/ClaimService'; + +const validation = [ + body('rewardId').isMongoId(), + body('claimAmount').isInt(), + body('redirectURL').isURL({ require_tld: false }), +]; + +const controller = async (req: Request, res: Response) => { + const rewardId = req.body.rewardId; + const redirectURL = req.body.redirectURL; + const claimAmount = Number(req.body.claimAmount); + + const reward = await RewardNFT.findById(rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + const entries = await QRCodeService.create({ rewardId, redirectURL }, claimAmount); + + res.status(201).json(entries); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/qr-codes/qr-codes.router.ts b/apps/api/src/app/controllers/qr-codes/qr-codes.router.ts new file mode 100644 index 000000000..8a7a4f044 --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/qr-codes.router.ts @@ -0,0 +1,33 @@ +import express from 'express'; +import { assertRequestInput, checkJwt, corsHandler, guard } from '@thxnetwork/api/middlewares'; +import * as ListEntry from './list.controller'; +import * as CreateEntry from './post.controller'; +import * as ReadEntry from './get.controller'; +import * as DeleteEntryController from './delete.controller'; +import * as ReadRedirectEntry from './redirect/get.controller'; +import * as UpdateEntryController from './collect/post.controller'; + +const router: express.Router = express.Router(); + +router.get('/:uuid', assertRequestInput(ReadEntry.validation), ReadEntry.controller); +router.get('/r/:uuid', assertRequestInput(ReadRedirectEntry.validation), ReadRedirectEntry.controller); + +router.use(checkJwt, corsHandler); +router.get('/', guard.check(['pools:read']), assertRequestInput(ListEntry.validation), ListEntry.controller); +router.post('/', guard.check(['pools:read']), assertRequestInput(CreateEntry.validation), CreateEntry.controller); + +router.patch( + '/:uuid', + guard.check(['claims:read']), + assertRequestInput(UpdateEntryController.validation), + UpdateEntryController.controller, +); + +router.delete( + '/:uuid', + guard.check(['pools:write']), + assertRequestInput(DeleteEntryController.validation), + DeleteEntryController.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/qr-codes/qr-codes.test.ts b/apps/api/src/app/controllers/qr-codes/qr-codes.test.ts new file mode 100644 index 000000000..d4d5bf15c --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/qr-codes.test.ts @@ -0,0 +1,192 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId, NFTVariant, RewardVariant } from '@thxnetwork/common/enums'; +import { sub, dashboardAccessToken, widgetAccessToken, widgetAccessToken2 } from '@thxnetwork/api/util/jest/constants'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { + PoolDocument, + ERC721Metadata, + ERC721MetadataDocument, + QRCodeEntryDocument, + ERC721Document, + RewardNFTDocument, +} from '@thxnetwork/api/models'; +import { IPFS_BASE_URL } from '@thxnetwork/api/config/secrets'; +import { safeVersion } from '@thxnetwork/api/services/ContractService'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { poll } from '@thxnetwork/api/util/polling'; +import { WalletDocument } from '@thxnetwork/api/models/Wallet'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import ERC721Service from '@thxnetwork/api/services/ERC721Service'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const user = request.agent(app); + +describe('QR Codes', () => { + let poolId: string, + pool: PoolDocument, + erc721: ERC721Document, + reward: RewardNFTDocument, + metadata: ERC721MetadataDocument, + wallet: WalletDocument, + qrcodes: QRCodeEntryDocument[]; + + const chainId = ChainId.Hardhat; + + beforeAll(async () => { + await beforeAllCallback(); + + pool = await PoolService.deploy(sub, 'My Reward Campaign'); + poolId = String(pool._id); + + const safe = await SafeService.create({ sub, chainId, safeVersion, poolId }); + + // Wait for campaign safe to be deployed + const { web3 } = getProvider(ChainId.Hardhat); + await poll( + () => web3.eth.getCode(safe.address), + (data: string) => data === '0x', + 1000, + ); + + erc721 = await ERC721Service.deploy({ + variant: NFTVariant.ERC721, + sub, + chainId, + name: 'Test Collection', + symbol: 'TST', + description: '', + baseURL: 'https://example.com', + archived: false, + logoImgUrl: 'https://img.url', + }); + metadata = await ERC721Metadata.create({ + erc721Id: String(erc721._id), + name: 'Token Silver', + image: IPFS_BASE_URL + 'abcdef', + imageUrl: 'https://image.com/image.jpg', + description: 'Lorem ipsum dolor sit amet', + externalUrl: 'https://example.com', + }); + wallet = await SafeService.findOne({ sub }); + }); + afterAll(afterAllCallback); + + it('POST /pools/:poolId/rewards/:variant', (done) => { + user.post(`/v1/pools/${poolId}/rewards/${RewardVariant.NFT}`) + .set({ Authorization: dashboardAccessToken }) + .send({ + title: '', + description: '', + pointPrice: 0, + limit: 0, + variant: RewardVariant.NFT, + erc721Id: erc721._id, + metadataId: metadata._id, + }) + .expect(({ body }: request.Response) => { + expect(body._id).toBeDefined(); + reward = body; + }) + .expect(201, done); + }); + + it('POST /qr-codes', (done) => { + user.post(`/v1/qr-codes`) + .set({ Authorization: dashboardAccessToken }) + .send({ + rewardId: reward._id, + claimAmount: 10, + redirectURL: 'https://example.com/redirect', + }) + .expect(({ body }: request.Response) => { + expect(body).toHaveLength(10); + }) + .expect(201, done); + }); + + it('GET /qr-codes?rewardId=:rewardId&page=:page&limit=:limit', (done) => { + user.get(`/v1/qr-codes`) + .set({ Authorization: dashboardAccessToken }) + .query({ + rewardId: reward._id, + page: 1, + limit: 15, + }) + .expect(({ body }: request.Response) => { + expect(body.total).toBe(10); + expect(body.results).toHaveLength(10); + qrcodes = body.results; + }) + .expect(200, done); + }); + + it('GET /qr-codes/:uuid', (done) => { + user.get(`/v1/qr-codes/${qrcodes[0].uuid}`) + .expect(({ body }: request.Response) => { + expect(body.entry).toBeDefined(); + expect(body.erc721).toBeDefined(); + expect(body.metadata).toBeDefined(); + }) + .expect(200, done); + }); + + it('PATCH /qr-codes/:uuid should succeed', (done) => { + user.patch(`/v1/qr-codes/${qrcodes[0].uuid}`) + .query({ walletId: String(wallet._id) }) + .set({ Authorization: widgetAccessToken }) + .expect(({ body }: request.Response) => { + expect(body.erc721).toBeDefined(); + expect(body.entry).toBeDefined(); + expect(body.payment).toBeDefined(); + expect(body.token).toBeDefined(); + expect(body.metadata).toBeDefined(); + expect(body.reward).toBeDefined(); + }) + .expect(200, done); + }); + + it('GET /qr-codes/:uuid should return sub', (done) => { + user.get(`/v1/qr-codes/${qrcodes[0].uuid}`) + .set({ Authorization: widgetAccessToken }) + .expect(({ body }: request.Response) => { + expect(body.entry).toBeDefined(); + expect(body.entry.sub).toBeDefined(); + expect(body.erc721).toBeDefined(); + expect(body.metadata).toBeDefined(); + }) + .expect(200, done); + }); + + it('PATCH /qr-codes/:uuid should fail', (done) => { + user.patch(`/v1/qr-codes/${qrcodes[0].uuid}`) + .query({ walletId: String(wallet._id) }) + .set({ Authorization: widgetAccessToken }) + .expect(({ body }: request.Response) => { + expect(body.error.message).toBe('This NFT is claimed already.'); + }) + .expect(403, done); + }); + + it('PATCH /qr-codes/:uuid from other account should also fail', (done) => { + user.patch(`/v1/qr-codes/${qrcodes[0].uuid}`) + .query({ walletId: String(wallet._id) }) + .set({ Authorization: widgetAccessToken2 }) + .expect(({ body }: request.Response) => { + expect(body.error.message).toBe('This NFT is claimed already.'); + }) + .expect(403, done); + }); + + it('First attempt other claim for other account should succeed', (done) => { + user.patch(`/v1/qr-codes/${qrcodes[1].uuid}`) + .query({ walletId: String(wallet._id) }) + .set({ Authorization: widgetAccessToken2 }) + .expect(({ body }: request.Response) => { + expect(body.entry).toBeDefined(); + expect(body.erc721).toBeDefined(); + expect(body.metadata).toBeDefined(); + }) + .expect(200, done); + }); +}); diff --git a/apps/api/src/app/controllers/qr-codes/redirect/get.controller.ts b/apps/api/src/app/controllers/qr-codes/redirect/get.controller.ts new file mode 100644 index 000000000..d85966835 --- /dev/null +++ b/apps/api/src/app/controllers/qr-codes/redirect/get.controller.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { RewardNFT, QRCodeEntry } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { WIDGET_URL } from '@thxnetwork/api/config/secrets'; + +const validation = [param('uuid').isUUID(4)]; + +const controller = async (req: Request, res: Response) => { + const entry = await QRCodeEntry.findOne({ uuid: req.params.uuid }); + if (!entry) throw new NotFoundError('QR code entry not found'); + + const reward = await RewardNFT.findById(entry.rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + // If redirectURL is set in entry, redirect immediately + if (entry.redirectURL) { + const url = new URL(entry.redirectURL); + url.searchParams.append('thx_widget_path', `/c/${req.params.uuid}`); + + return res.redirect(302, url.toString()); + } + + // Redirect to campaign URl if not present in the entry + const pool = await PoolService.getById(reward.poolId); + if (!pool) throw new NotFoundError('Pool not found.'); + + const url = new URL(WIDGET_URL); + url.pathname = `/c/${pool.settings.slug}/c/${req.params.uuid}`; + + return res.redirect(302, url.toString()); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/custom/custom.router.ts b/apps/api/src/app/controllers/quests/custom/custom.router.ts new file mode 100644 index 000000000..f2f9e9662 --- /dev/null +++ b/apps/api/src/app/controllers/quests/custom/custom.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import * as CreateEntry from './entries/post.controller'; +import { assertRequestInput, assertAccount } from '@thxnetwork/api/middlewares'; +import { limitInSeconds } from '@thxnetwork/api/util/ratelimiter'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post( + '/:id/entries', + limitInSeconds(3), + assertRequestInput(CreateEntry.validation), + assertAccount, + CreateEntry.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/quests/custom/custom.test.ts b/apps/api/src/app/controllers/quests/custom/custom.test.ts new file mode 100644 index 000000000..16c2328f6 --- /dev/null +++ b/apps/api/src/app/controllers/quests/custom/custom.test.ts @@ -0,0 +1,84 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { v4 } from 'uuid'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import { dashboardAccessToken, userWalletAddress2, widgetAccessToken2 } from '@thxnetwork/api/util/jest/constants'; +import { isAddress } from 'web3-utils'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { PoolDocument, QuestCustom } from '@thxnetwork/api/models'; + +const user = request.agent(app); + +describe('Quests Custom ', () => { + let pool: PoolDocument, customQuest: TQuestCustom; + const eventName = v4(); + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + it('POST /pools', (done) => { + user.post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send() + .expect((res: request.Response) => { + expect(isAddress(res.body.safeAddress)).toBe(true); + pool = res.body; + }) + .expect(201, done); + }); + + it('POST /pools/:id/quests', (done) => { + user.post(`/v1/pools/${pool._id}/quests/${QuestVariant.Custom}`) + .set({ Authorization: dashboardAccessToken }) + .send({ + variant: QuestVariant.Custom, + title: 'Expiration date is next 30 min', + description: 'Lorem ipsum dolor sit amet', + amount: 100, + limit: 1, + index: 0, + eventName, + }) + .expect(async (res: request.Response) => { + expect(res.body.uuid).toBeDefined(); + expect(res.body.amount).toBe(100); + customQuest = res.body; + await QuestCustom.findByIdAndUpdate(customQuest._id, { eventName: customQuest.uuid }); + }) + .expect(201, done); + }); + + describe('Qualify (to be deprecated)', () => { + it('POST /webhook/milestone/:token/claim', (done) => { + user.post(`/v1/webhook/milestone/${customQuest.uuid}/claim`) + .send({ + address: userWalletAddress2, + }) + .expect(201, done); + }); + + it('POST /webhook/milestone/:token/claim second time should also succeed', (done) => { + user.post(`/v1/webhook/milestone/${customQuest.uuid}/claim`) + .send({ + address: userWalletAddress2, + }) + .expect(201, done); + }); + }); + + describe('Collect', () => { + it('GET /account to update identity', (done) => { + user.get(`/v1/account`) + .set({ 'X-PoolId': pool._id, 'Authorization': widgetAccessToken2 }) + .expect(200, done); + }); + + it('POST /quests/custom/:id/entries', async () => { + const { status } = await user + .post(`/v1/quests/custom/${customQuest._id}/entries`) + .set({ 'X-PoolId': pool._id, 'Authorization': widgetAccessToken2 }) + .send({ recaptcha: 'test' }); + expect(status).toBe(200); + }); + }); +}); diff --git a/apps/api/src/app/controllers/quests/custom/entries/post.controller.ts b/apps/api/src/app/controllers/quests/custom/entries/post.controller.ts new file mode 100644 index 000000000..bec226ac7 --- /dev/null +++ b/apps/api/src/app/controllers/quests/custom/entries/post.controller.ts @@ -0,0 +1,38 @@ +import { Request, Response } from 'express'; +import { QuestCustom } from '@thxnetwork/api/models'; +import { body, param } from 'express-validator'; +import { JobType, QuestVariant } from '@thxnetwork/common/enums'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +const validation = [param('id').isMongoId(), body('recaptcha').isString()]; + +const controller = async ({ params, body, account }: Request, res: Response) => { + const quest = await QuestCustom.findById(params.id); + if (!quest) throw new NotFoundError('Quest not found.'); + + const data = { recaptcha: body.recaptcha }; + + // Running separately to avoid issues when getting validation results from Discord interactions + const isRealUser = await QuestService.isRealUser(quest.variant, { quest, account, data }); + if (!isRealUser.result) return res.json({ error: isRealUser.reason }); + + const { result, reason } = await QuestService.getValidationResult(quest.variant, { + quest, + account, + data, + }); + if (!result) return res.json({ error: reason }); + + const job = await agenda.now(JobType.CreateQuestEntry, { + variant: QuestVariant.Custom, + questId: String(quest._id), + sub: account.sub, + data: { ...data, isClaimed: true }, + }); + + res.json({ jobId: job.attrs._id }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/daily/daily.router.ts b/apps/api/src/app/controllers/quests/daily/daily.router.ts new file mode 100644 index 000000000..09334a3ef --- /dev/null +++ b/apps/api/src/app/controllers/quests/daily/daily.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import * as CreateEntry from './entries/post.controller'; +import { assertAccount, assertRequestInput } from '@thxnetwork/api/middlewares'; +import { limitInSeconds } from '@thxnetwork/api/util/ratelimiter'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post( + '/:id/entries', + limitInSeconds(3), + assertRequestInput(CreateEntry.validation), + assertAccount, + CreateEntry.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/quests/daily/entries/post.controller.ts b/apps/api/src/app/controllers/quests/daily/entries/post.controller.ts new file mode 100644 index 000000000..89ebe229f --- /dev/null +++ b/apps/api/src/app/controllers/quests/daily/entries/post.controller.ts @@ -0,0 +1,40 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { JobType, QuestVariant } from '@thxnetwork/common/enums'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import { QuestDaily } from '@thxnetwork/api/models'; +import { getIP } from '@thxnetwork/api/util/ip'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const validation = [param('id').isMongoId(), body('recaptcha').isString()]; + +const controller = async (req: Request, res: Response) => { + const { params, account } = req; + const quest = await QuestDaily.findById(params.id); + if (!quest) throw new NotFoundError('Could not find the Daily Reward'); + + // Only do this is no event requirement is set + const data = { recaptcha: req.body.recaptcha, metadata: {} }; + if (!quest.eventName) { + data.metadata['ip'] = getIP(req); + } + + // Running separately to avoid issues when getting validation results from Discord interactions + const isRealUser = await QuestService.isRealUser(quest.variant, { quest, account, data }); + if (!isRealUser.result) return res.json({ error: isRealUser.reason }); + + const { result, reason } = await QuestService.getValidationResult(quest.variant, { quest, account, data }); + if (!result) return res.json({ error: reason }); + + const job = await agenda.now(JobType.CreateQuestEntry, { + variant: QuestVariant.Daily, + questId: String(quest._id), + sub: account.sub, + data, + }); + + res.json({ jobId: job.attrs._id }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/gitcoin/entries/post.controller.ts b/apps/api/src/app/controllers/quests/gitcoin/entries/post.controller.ts new file mode 100644 index 000000000..832bd3589 --- /dev/null +++ b/apps/api/src/app/controllers/quests/gitcoin/entries/post.controller.ts @@ -0,0 +1,50 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { recoverSigner } from '@thxnetwork/api/util/network'; +import { JobType, QuestVariant } from '@thxnetwork/common/enums'; +import { QuestGitcoin } from '@thxnetwork/api/models'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import GitcoinService from '@thxnetwork/api/services/GitcoinService'; + +const validation = [ + param('id').isMongoId(), + body('signature').isString(), + body('chainId').isInt(), + body('recaptcha').isString(), +]; + +const controller = async ({ account, body, params }: Request, res: Response) => { + const quest = await QuestGitcoin.findById(params.id); + if (!quest) throw new NotFoundError('Quest not found'); + + const address = recoverSigner(body.message, body.signature); + const data = { recaptcha: body.recaptcha, metadata: { address } }; + + // Running separately to avoid issues when getting validation results from Discord interactions + const isRealUser = await QuestService.isRealUser(quest.variant, { quest, account, data }); + if (!isRealUser.result) return res.json({ error: isRealUser.reason }); + + // Add wallet and add score for address + const { score, error } = await GitcoinService.getScoreUniqueHumanity( + quest.scorerId, + data.metadata.address.toLowerCase(), + ); + if (error) return res.json({ error }); + data.metadata['score'] = score; + + const { result, reason } = await QuestService.getValidationResult(quest.variant, { quest, account, data }); + if (!result) return res.json({ error: reason }); + + const job = await agenda.now(JobType.CreateQuestEntry, { + variant: QuestVariant.Gitcoin, + questId: String(quest._id), + sub: account.sub, + data, + }); + + res.json({ jobId: job.attrs._id }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/gitcoin/gitcoin.router.ts b/apps/api/src/app/controllers/quests/gitcoin/gitcoin.router.ts new file mode 100644 index 000000000..f2f9e9662 --- /dev/null +++ b/apps/api/src/app/controllers/quests/gitcoin/gitcoin.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import * as CreateEntry from './entries/post.controller'; +import { assertRequestInput, assertAccount } from '@thxnetwork/api/middlewares'; +import { limitInSeconds } from '@thxnetwork/api/util/ratelimiter'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post( + '/:id/entries', + limitInSeconds(3), + assertRequestInput(CreateEntry.validation), + assertAccount, + CreateEntry.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/quests/list.controller.ts b/apps/api/src/app/controllers/quests/list.controller.ts new file mode 100644 index 000000000..82869975b --- /dev/null +++ b/apps/api/src/app/controllers/quests/list.controller.ts @@ -0,0 +1,38 @@ +import { Request, Response } from 'express'; +import { getIP } from '@thxnetwork/api/util/ip'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import { parseToken } from '@thxnetwork/api/util/jwt'; + +// This endpoint is public so we do not get req.auth populated +// so we need to decode the auth header manually when it is present +const controller = async (req: Request, res: Response) => { + const token = parseToken(req.header('authorization')); + const sub = token && token.sub; + + const pool = await PoolService.getById(req.header('X-PoolId')); + const account = sub && (await AccountProxy.findById(sub)); + + const ip = getIP(req); + // Results are returned in order of the QuestVariant enum keys + const [daily, invite, twitter, discord, youtube, custom, web3, gitcoin, webhook] = await QuestService.list({ + pool, + account, + data: { ip }, + }); + + res.json({ + daily, + custom, + invite, + twitter, + discord, + youtube, + web3, + gitcoin, + webhook, + }); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/quests/quests.router.ts b/apps/api/src/app/controllers/quests/quests.router.ts new file mode 100644 index 000000000..8d3a44425 --- /dev/null +++ b/apps/api/src/app/controllers/quests/quests.router.ts @@ -0,0 +1,24 @@ +import { checkJwt, corsHandler } from '@thxnetwork/api/middlewares'; +import express from 'express'; +import * as ListQuests from './list.controller'; +import * as ListQuestsPublic from './recent/list.controller'; +import RouterQuestSocial from './social/social.router'; +import RouterQuestWeb3 from './web3/web3.router'; +import RouterQuestGitcoin from './gitcoin/gitcoin.router'; +import RouterQuestDaily from './daily/daily.router'; +import RouterQuestCustom from './custom/custom.router'; +import RouterQuestWebhook from './webhook/webhook.router'; + +const router: express.Router = express.Router(); + +router.get('/', ListQuests.controller); +router.get('/public', ListQuestsPublic.controller); +router.use(checkJwt).use(corsHandler); +router.use('/social', RouterQuestSocial); +router.use('/web3', RouterQuestWeb3); +router.use('/gitcoin', RouterQuestGitcoin); +router.use('/daily', RouterQuestDaily); +router.use('/custom', RouterQuestCustom); +router.use('/webhook', RouterQuestWebhook); + +export default router; diff --git a/apps/api/src/app/controllers/quests/recent/list.controller.ts b/apps/api/src/app/controllers/quests/recent/list.controller.ts new file mode 100644 index 000000000..9eba84e02 --- /dev/null +++ b/apps/api/src/app/controllers/quests/recent/list.controller.ts @@ -0,0 +1,99 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { + Pool, + QuestDaily, + QuestInvite, + QuestSocial, + QuestCustom, + QuestWeb3, + QuestGitcoin, +} from '@thxnetwork/api/models'; +const validation = [query('page').isInt(), query('limit').isInt(), query('search').optional().isString()]; + +const controller = async (req: Request, res: Response) => { + const questModels = [QuestDaily, QuestInvite, QuestSocial, QuestCustom, QuestWeb3, QuestGitcoin]; + const questLookupStages = questModels.map((model) => { + return { + $lookup: { + from: model.collection.name, + localField: 'poolId', + foreignField: 'poolId', + as: model.collection.name, + }, + }; + }); + + const decoratedPools = await Pool.aggregate([ + { + $addFields: { + poolId: { + $convert: { + input: '$_id', + to: 'string', + }, + }, + }, + }, + ...questLookupStages, + { + $lookup: { + from: 'widget', + localField: 'poolId', + foreignField: 'poolId', + as: 'widget', + }, + }, + { + $lookup: { + from: 'brand', + localField: 'poolId', + foreignField: 'poolId', + as: 'brand', + }, + }, + { + $match: { + 'settings.isPublished': true, + 'rank': { $exists: true }, + }, + }, + ]).exec(); + + const sortByDate = (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); + + const result = decoratedPools + // Format and sort all quests per pool + .map((result) => { + const mapper = (q) => ({ + ...q, + amount: q.amounts ? q.amounts[q.amounts.length - 1] : q.amount, + widget: result.widget && result.widget[0], + domain: result.widget && result.widget[0] && result.widget[0].domain, + brand: result.brand && result.brand[0], + }); + + const quests = [ + ...result[QuestDaily.collection.name].map(mapper).sort(sortByDate), + ...result[QuestInvite.collection.name].map(mapper).sort(sortByDate), + ...result[QuestSocial.collection.name].map(mapper).sort(sortByDate), + ...result[QuestCustom.collection.name].map(mapper).sort(sortByDate), + ...result[QuestWeb3.collection.name].map(mapper).sort(sortByDate), + ...result[QuestGitcoin.collection.name].map(mapper).sort(sortByDate), + ]; + + return { + quests, + }; + }) + // Last quest per pool + .map((pool) => pool.quests[0]) + // Sort by createdAt + .sort(sortByDate) + // Cut of first 4 results + .slice(0, 4); + + res.json(result); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/social/entries/post.controller.ts b/apps/api/src/app/controllers/quests/social/entries/post.controller.ts new file mode 100644 index 000000000..0450a969e --- /dev/null +++ b/apps/api/src/app/controllers/quests/social/entries/post.controller.ts @@ -0,0 +1,68 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { JobType, agenda } from '@thxnetwork/api/util/agenda'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import { TwitterUser } from '@thxnetwork/api/models/TwitterUser'; +import { DiscordMessage, DiscordReaction, QuestSocial } from '@thxnetwork/api/models'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import DiscordService from '@thxnetwork/api/services/DiscordService'; +import { QuestSocialRequirement } from '@thxnetwork/common/enums'; + +const validation = [param('id').isMongoId(), body('recaptcha').isString()]; + +async function controller({ params, body, account }: Request, res: Response) { + // Get the quest document + const quest = await QuestSocial.findById(params.id); + if (!quest) throw new NotFoundError('Quest not found'); + + // Get platform user id for account + const platformUserId = QuestService.findUserIdForInteraction(account, quest.interaction); + if (!platformUserId) return res.json({ error: 'Could not find platform user id.' }); + + const data = { metadata: { platformUserId, discord: {}, twitter: {} }, recaptcha: body.recaptcha }; + + // Running separately to avoid issues when getting validation results from Discord interactions + const isRealUser = await QuestService.isRealUser(quest.variant, { quest, account, data }); + if (!isRealUser.result) return res.json({ error: isRealUser.reason }); + + // Get validation result for this quest entry + const { result, reason } = await QuestService.getValidationResult(quest.variant, { quest, account, data }); + if (!result) return res.json({ error: reason }); + + // For Discord Bot quests we store server user name in metadata + if (quest.variant === QuestVariant.Discord && quest.interaction !== QuestSocialRequirement.DiscordGuildJoined) { + const guild = await DiscordService.getGuild(quest.poolId); + const member = guild && (await DiscordService.getMember(guild.id, platformUserId)); + + data.metadata.discord = { + guildId: guild && guild.id, + username: member.user.username, + joinedAt: new Date(member.joinedTimestamp).toISOString(), + reactionCount: guild + ? await DiscordReaction.countDocuments({ guildId: guild.id, userId: platformUserId }) + : 0, + messageCount: guild + ? await DiscordMessage.countDocuments({ guildId: guild.id, userId: platformUserId }) + : 0, + }; + } + + // For Twitter quests we store public metrics in metadata + if (quest.variant === QuestVariant.Twitter) { + const user = await TwitterUser.findOne({ userId: platformUserId }); + data.metadata.twitter = user.publicMetrics; + } + + // Schedule serial job + const job = await agenda.now(JobType.CreateQuestEntry, { + variant: quest.variant, + questId: String(quest._id), + sub: account.sub, + data, + }); + + res.json({ jobId: job.attrs._id }); +} + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/social/social.router.ts b/apps/api/src/app/controllers/quests/social/social.router.ts new file mode 100644 index 000000000..72c309e5a --- /dev/null +++ b/apps/api/src/app/controllers/quests/social/social.router.ts @@ -0,0 +1,16 @@ +import express, { Router } from 'express'; +import { assertRequestInput, assertAccount } from '@thxnetwork/api/middlewares'; +import { limitInSeconds } from '@thxnetwork/api/util/ratelimiter'; +import * as CreateEntries from './entries/post.controller'; + +const router: express.Router = Router({ mergeParams: true }); + +router.post( + '/:id/entries', + limitInSeconds(3), + assertRequestInput(CreateEntries.validation), + assertAccount, + CreateEntries.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/quests/web3/entries/post.controller.ts b/apps/api/src/app/controllers/quests/web3/entries/post.controller.ts new file mode 100644 index 000000000..6ba050221 --- /dev/null +++ b/apps/api/src/app/controllers/quests/web3/entries/post.controller.ts @@ -0,0 +1,59 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { QuestWeb3 } from '@thxnetwork/api/models/QuestWeb3'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import { recoverSigner } from '@thxnetwork/api/util/network'; +import { chainList } from '@thxnetwork/common/chains'; +import { JobType, QuestVariant } from '@thxnetwork/common/enums'; +import QuestWeb3Service from '@thxnetwork/api/services/QuestWeb3Service'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const validation = [ + param('id').isMongoId(), + body('signature').isString(), + body('chainId').isInt(), + body('recaptcha').isString(), +]; + +const controller = async ({ account, body, params }: Request, res: Response) => { + const quest = await QuestWeb3.findById(params.id); + if (!quest) throw new NotFoundError('Quest not found'); + + const address = recoverSigner(body.message, body.signature); + if (!address) throw new NotFoundError(`Could not recover address from signature.`); + + const { rpc, name } = chainList[body.chainId]; + if (!rpc) throw new NotFoundError(`Could not find RPC for ${name}`); + + const data = { recaptcha: body.recaptcha, metadata: { address, rpc, chainId: body.chainId, callResult: '' } }; + + // Running separately to avoid issues when getting validation results from Discord interactions + const isRealUser = await QuestService.isRealUser(quest.variant, { quest, account, data }); + if (!isRealUser.result) return res.json({ error: isRealUser.reason }); + + // Fetch the call result so we can store it in the entry + const callResult = await QuestWeb3Service.getCallResult({ quest, account, data }); + if (!callResult.result) return res.json({ error: callResult.reason }); + data.metadata.callResult = callResult.value.toString(); + + // Validate the result + const validationResult = await QuestService.getValidationResult(quest.variant, { + quest, + account, + data, + }); + if (!validationResult.result) return res.json({ error: validationResult.reason }); + + // Schedule the job + const job = await agenda.now(JobType.CreateQuestEntry, { + variant: QuestVariant.Web3, + questId: String(quest._id), + sub: account.sub, + data, + }); + + res.json({ jobId: job.attrs._id }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/web3/web3.router.ts b/apps/api/src/app/controllers/quests/web3/web3.router.ts new file mode 100644 index 000000000..82a49522a --- /dev/null +++ b/apps/api/src/app/controllers/quests/web3/web3.router.ts @@ -0,0 +1,10 @@ +import express from 'express'; +import * as Create from './entries/post.controller'; +import { assertAccount, assertRequestInput } from '@thxnetwork/api/middlewares'; +import { limitInSeconds } from '@thxnetwork/api/util/ratelimiter'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post('/:id/entries', limitInSeconds(3), assertRequestInput(Create.validation), assertAccount, Create.controller); + +export default router; diff --git a/apps/api/src/app/controllers/quests/webhook/entries/post.controller.ts b/apps/api/src/app/controllers/quests/webhook/entries/post.controller.ts new file mode 100644 index 000000000..d0b6cf6ba --- /dev/null +++ b/apps/api/src/app/controllers/quests/webhook/entries/post.controller.ts @@ -0,0 +1,42 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import { JobType, QuestVariant } from '@thxnetwork/common/enums'; +import { QuestWebhook } from '@thxnetwork/api/models'; +import QuestService from '@thxnetwork/api/services/QuestService'; + +const validation = [param('id').isMongoId()]; + +const controller = async ({ account, body, params }: Request, res: Response) => { + const quest = await QuestWebhook.findById(params.id); + if (!quest) throw new NotFoundError('Quest not found'); + + const data = { + recaptcha: body.recaptcha, + }; + + // Running separately to avoid issues when getting validation results from Discord interactions + const isRealUser = await QuestService.isRealUser(quest.variant, { quest, account, data }); + if (!isRealUser.result) return res.json({ error: isRealUser.reason }); + + // Validate the result + const validationResult = await QuestService.getValidationResult(quest.variant, { + quest, + account, + data, + }); + if (!validationResult.result) return res.json({ error: validationResult.reason }); + + // Schedule the job + const job = await agenda.now(JobType.CreateQuestEntry, { + variant: QuestVariant.Webhook, + questId: String(quest._id), + sub: account.sub, + data: { ...data, metadata: validationResult }, + }); + + res.json({ jobId: job.attrs._id }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/quests/webhook/webhook.router.ts b/apps/api/src/app/controllers/quests/webhook/webhook.router.ts new file mode 100644 index 000000000..08f4c714c --- /dev/null +++ b/apps/api/src/app/controllers/quests/webhook/webhook.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import { assertAccount, assertRequestInput } from '@thxnetwork/api/middlewares'; +import { limitInSeconds } from '@thxnetwork/api/util/ratelimiter'; +import * as CreateEntry from './entries/post.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.post( + '/:id/entries', + limitInSeconds(3), + assertRequestInput(CreateEntry.validation), + assertAccount, + CreateEntry.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/rewards/list.controller.ts b/apps/api/src/app/controllers/rewards/list.controller.ts new file mode 100644 index 000000000..a8815d7ab --- /dev/null +++ b/apps/api/src/app/controllers/rewards/list.controller.ts @@ -0,0 +1,22 @@ +import { Request, Response } from 'express'; +import { parseToken } from '@thxnetwork/api/util/jwt'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import RewardService from '@thxnetwork/api/services/RewardService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; + +const controller = async (req: Request, res: Response) => { + const token = parseToken(req.header('authorization')); + const sub = token && token.sub; + + const pool = await PoolService.getById(req.header('X-PoolId')); + const account = sub && (await AccountProxy.findById(sub)); + + const [coin, nft, custom, coupon, discordRole, galachain] = await RewardService.list({ + pool, + account, + }); + + res.json({ coin, nft, custom, coupon, discordRole, galachain }); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/rewards/payments/list.controller.ts b/apps/api/src/app/controllers/rewards/payments/list.controller.ts new file mode 100644 index 000000000..ce90a1944 --- /dev/null +++ b/apps/api/src/app/controllers/rewards/payments/list.controller.ts @@ -0,0 +1,12 @@ +import RewardService from '@thxnetwork/api/services/RewardService'; +import { Request, Response } from 'express'; +import { query } from 'express-validator'; + +const validation = [query('walletId').optional().isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const payments = await RewardService.findPaymentsForSub(req.auth.sub); + res.json(payments); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/rewards/payments/post.controller.ts b/apps/api/src/app/controllers/rewards/payments/post.controller.ts new file mode 100644 index 000000000..13957387b --- /dev/null +++ b/apps/api/src/app/controllers/rewards/payments/post.controller.ts @@ -0,0 +1,47 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { JobType, RewardVariant } from '@thxnetwork/common/enums'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import RewardService from '@thxnetwork/api/services/RewardService'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import { Wallet } from '@thxnetwork/api/models'; + +const validation = [param('variant').isInt(), param('rewardId').isMongoId(), body('walletId').optional().isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const variant = req.params.variant as unknown as RewardVariant; + const rewardId = req.params.rewardId as string; + + const reward = await RewardService.findById(variant, rewardId); + if (!reward) throw new NotFoundError('Reward not found'); + + const pool = await PoolService.getById(reward.poolId); + if (!pool) throw new NotFoundError('Campaign not found'); + + const safe = await SafeService.findOneByPool(pool); + if (!safe) throw new NotFoundError('Campaign Safe not found'); + + const account = await AccountProxy.findById(req.auth.sub); + if (!account) throw new NotFoundError('Account not found'); + + const wallet = req.body.walletId ? await Wallet.findById(req.body.walletId) : null; + const validationResult = await RewardService.getValidationResult({ reward, account, safe, wallet }); + if (!validationResult.result) { + throw new ForbiddenError(validationResult.reason); + } + + // Serialize payment processing with job queue + const job = await agenda.now(JobType.CreateRewardPayment, { + variant, + rewardId, + sub: account.sub, + walletId: req.body.walletId, + }); + + res.json({ jobId: job.attrs._id }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/rewards/rewards.router.ts b/apps/api/src/app/controllers/rewards/rewards.router.ts new file mode 100644 index 000000000..059aeb181 --- /dev/null +++ b/apps/api/src/app/controllers/rewards/rewards.router.ts @@ -0,0 +1,18 @@ +import { assertRequestInput, checkJwt, corsHandler } from '@thxnetwork/api/middlewares'; +import express, { Router } from 'express'; +import * as ListRewards from './list.controller'; +import * as CreateRewardPayment from './payments/post.controller'; +import * as ListRewardPayment from './payments/list.controller'; + +const router: express.Router = express.Router({ mergeParams: true }); + +router.get('/', ListRewards.controller); +router.use(checkJwt, corsHandler); +router.post( + '/:variant/:rewardId/payments', + assertRequestInput(CreateRewardPayment.validation), + CreateRewardPayment.controller, +); +router.get('/payments', assertRequestInput(ListRewardPayment.validation), ListRewardPayment.controller); + +export default router; diff --git a/apps/api/src/app/controllers/token/cs/get.controller.ts b/apps/api/src/app/controllers/token/cs/get.controller.ts new file mode 100644 index 000000000..fc8a3444c --- /dev/null +++ b/apps/api/src/app/controllers/token/cs/get.controller.ts @@ -0,0 +1,8 @@ +import { Request, Response } from 'express'; +import { CIRCULATING_SUPPLY } from '@thxnetwork/api/config/secrets'; + +const controller = async (req: Request, res: Response) => { + res.header('Content-Type', 'text/plain').send(CIRCULATING_SUPPLY); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/token/token.router.ts b/apps/api/src/app/controllers/token/token.router.ts new file mode 100644 index 000000000..0865ea18b --- /dev/null +++ b/apps/api/src/app/controllers/token/token.router.ts @@ -0,0 +1,11 @@ +import express, { Router } from 'express'; + +import * as ReadTokenCirculatingSupply from './cs/get.controller'; +import * as ReadTokenTotalSupply from './ts/get.controller'; + +const router: express.Router = express.Router(); + +router.get('/cs', ReadTokenCirculatingSupply.controller); +router.get('/ts', ReadTokenTotalSupply.controller); + +export default router; diff --git a/apps/api/src/app/controllers/token/ts/get.controller.ts b/apps/api/src/app/controllers/token/ts/get.controller.ts new file mode 100644 index 000000000..c34b4eb73 --- /dev/null +++ b/apps/api/src/app/controllers/token/ts/get.controller.ts @@ -0,0 +1,8 @@ +import { Request, Response } from 'express'; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['THX Token'] + res.header('Content-Type', 'text/plain').send('100000000'); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/transactions/list.controller.ts b/apps/api/src/app/controllers/transactions/list.controller.ts new file mode 100644 index 000000000..f6f9deaf0 --- /dev/null +++ b/apps/api/src/app/controllers/transactions/list.controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import { Wallet } from '@thxnetwork/api/models'; +import { ChainId } from '@thxnetwork/common/enums'; +import { NODE_ENV } from '@thxnetwork/api/config/secrets'; + +const validation = [query('chainId').optional().isNumeric(), query('poolId').optional().isString()]; + +const controller = async (req: Request, res: Response) => { + const chainId = NODE_ENV === 'production' ? ChainId.Polygon : ChainId.Hardhat; + const wallets = await Wallet.find({ + sub: req.auth.sub, + chainId, + safeVersion: { $exists: true }, + poolId: req.query.poolId, + }); + + res.json(wallets); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/upload/put.controller.ts b/apps/api/src/app/controllers/upload/put.controller.ts new file mode 100644 index 000000000..25315103c --- /dev/null +++ b/apps/api/src/app/controllers/upload/put.controller.ts @@ -0,0 +1,10 @@ +import { Request, Response } from 'express'; +import ImageService from '@thxnetwork/api/services/ImageService'; + +const controller = async (req: Request, res: Response) => { + if (!req.file) return res.status(440).send('There no file to process'); + const publicUrl = await ImageService.upload(req.file); + res.send({ publicUrl }); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/upload/upload.router.ts b/apps/api/src/app/controllers/upload/upload.router.ts new file mode 100644 index 000000000..e513f42d5 --- /dev/null +++ b/apps/api/src/app/controllers/upload/upload.router.ts @@ -0,0 +1,9 @@ +import express, { Router } from 'express'; +import { upload } from '@thxnetwork/api/util/multer'; +import * as PutUpload from './put.controller'; + +const router: express.Router = express.Router(); + +router.put('/', upload.single('file'), PutUpload.controller); + +export default router; diff --git a/apps/api/src/app/controllers/ve/claim/post.controller.ts b/apps/api/src/app/controllers/ve/claim/post.controller.ts new file mode 100644 index 000000000..6e95be582 --- /dev/null +++ b/apps/api/src/app/controllers/ve/claim/post.controller.ts @@ -0,0 +1,14 @@ +import { Request, Response } from 'express'; +import { query } from 'express-validator'; +import VoteEscrowService from '@thxnetwork/api/services/VoteEscrowService'; + +const validation = [query('walletId').isMongoId()]; + +const controller = async ({ wallet }: Request, res: Response) => { + // TODO Check if wallet has tokens to claim + // Propose the claimTokens transaction + const txs = await VoteEscrowService.claimTokens(wallet); + + res.status(201).json(txs); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/ve/deposit/post.controller.ts b/apps/api/src/app/controllers/ve/deposit/post.controller.ts new file mode 100644 index 000000000..edf43a092 --- /dev/null +++ b/apps/api/src/app/controllers/ve/deposit/post.controller.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +import { body, query } from 'express-validator'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { BigNumber } from 'alchemy-sdk'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { contractNetworks } from '@thxnetwork/api/hardhat'; +import VoteEscrowService from '@thxnetwork/api/services/VoteEscrowService'; + +const validation = [body('amountInWei').isString(), body('lockEndTimestamp').isInt(), query('walletId').isMongoId()]; + +const controller = async ({ body, wallet }: Request, res: Response) => { + // Check sufficient BPTGauge approval + const amount = await VoteEscrowService.getAllowance( + wallet, + contractNetworks[wallet.chainId].BPTGauge, + contractNetworks[wallet.chainId].VotingEscrow, + ); + if (BigNumber.from(amount).lt(body.amountInWei)) throw new ForbiddenError('Insufficient allowance'); + + // Check lockEndTimestamp to be more than today + 3 months + const { web3 } = getProvider(); + const latest = await web3.eth.getBlockNumber(); + const now = (await web3.eth.getBlock(latest)).timestamp; + if (now > body.lockEndTimestamp) throw new ForbiddenError('lockEndTimestamp needs be larger than today'); + + // Check SmartWalletWhitelist + const isApproved = await VoteEscrowService.isApprovedAddress(wallet.address, wallet.chainId); + if (!isApproved) throw new ForbiddenError('Wallet address is not on whitelist.'); + + // Deposit funds for wallet + const tx = await VoteEscrowService.deposit(wallet, body.amountInWei, body.lockEndTimestamp); + + res.status(201).json([tx]); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/ve/increase/post.controller.ts b/apps/api/src/app/controllers/ve/increase/post.controller.ts new file mode 100644 index 000000000..53150979d --- /dev/null +++ b/apps/api/src/app/controllers/ve/increase/post.controller.ts @@ -0,0 +1,56 @@ +import { Request, Response } from 'express'; +import { body, query } from 'express-validator'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { BigNumber } from 'alchemy-sdk'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { contractNetworks } from '@thxnetwork/api/hardhat'; +import VoteEscrowService from '@thxnetwork/api/services/VoteEscrowService'; + +const validation = [ + query('walletId').isMongoId(), + body('amountInWei').optional().isString(), + body('lockEndTimestamp').optional().isInt(), +]; + +const controller = async ({ wallet, body }: Request, res: Response) => { + // Check SmartWalletWhitelist + const isApproved = await VoteEscrowService.isApprovedAddress(wallet.address, wallet.chainId); + if (!isApproved) throw new ForbiddenError('Wallet address is not on whitelist.'); + + const txList = []; + if (body.amountInWei) { + // Check sufficient BPTGauge approval + const amount = await VoteEscrowService.getAllowance( + wallet, + contractNetworks[wallet.chainId].BPTGauge, + contractNetworks[wallet.chainId].VotingEscrow, + ); + if (BigNumber.from(amount).lt(body.amountInWei)) throw new ForbiddenError('Insufficient allowance'); + + // TODO Check sufficient balance + const txs = await VoteEscrowService.increaseAmount(wallet, body.amountInWei); + txList.push(txs); + } + + if (body.lockEndTimestamp) { + // Check lockEndTimestamp to be more than today + 90 days + const { web3 } = getProvider(); + const latest = await web3.eth.getBlockNumber(); + const now = (await web3.eth.getBlock(latest)).timestamp; + if (body.lockEndTimestamp < now) { + throw new ForbiddenError('lockEndTimestamp needs be larger than today'); + } + + // Check if lockEndTimestamp is more than current lock end + const lock = await VoteEscrowService.list(wallet); + if (body.lockEndTimestamp < Number(lock.end)) { + throw new ForbiddenError('lockEndTimestamp needs be larger than current lock end'); + } + + const txs = await VoteEscrowService.increaseUnlockTime(wallet, body.lockEndTimestamp); + txList.push(txs); + } + + res.status(201).json(txList); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/ve/list.controller.ts b/apps/api/src/app/controllers/ve/list.controller.ts new file mode 100644 index 000000000..49c406db4 --- /dev/null +++ b/apps/api/src/app/controllers/ve/list.controller.ts @@ -0,0 +1,34 @@ +import { Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { query } from 'express-validator'; +import WalletService from '@thxnetwork/api/services/WalletService'; +import VoteEscrowService from '@thxnetwork/api/services/VoteEscrowService'; +const parseMs = (s) => Number(s) * 1000; + +const validation = [query('walletId').isMongoId()]; +const controller = async (req: Request, res: Response) => { + const walletId = req.query.walletId as string; + const wallet = await WalletService.findById(walletId); + if (!wallet) throw new NotFoundError('Wallet not found.'); + + const { web3 } = getProvider(wallet.chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + + // Check for lock and determine ve fn to call + // Get veTHX balance and pending rewards + const [{ amount, end }, latestBlock, balance, rewards] = await Promise.all([ + VoteEscrowService.list(wallet), + web3.eth.getBlock('latest'), + ve.methods.balanceOf(wallet.address).call(), + VoteEscrowService.listRewards(wallet), + ]); + + res.json([{ balance, amount, end: parseMs(end), now: parseMs(latestBlock.timestamp), rewards }]); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/ve/ve.router.ts b/apps/api/src/app/controllers/ve/ve.router.ts new file mode 100644 index 000000000..b54064f7d --- /dev/null +++ b/apps/api/src/app/controllers/ve/ve.router.ts @@ -0,0 +1,20 @@ +import express from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import { assertWallet } from '@thxnetwork/api/middlewares/assertWallet'; + +import * as ListController from './list.controller'; +import * as CreateVEDeposit from './deposit/post.controller'; +import * as CreateVEIncrease from './increase/post.controller'; +import * as CreateVEClaim from './claim/post.controller'; +import * as CreateVEWithdraw from './withdraw/post.controller'; + +const router: express.Router = express.Router(); + +router.use('/', assertWallet); +router.get('/', assertRequestInput(ListController.validation), ListController.controller); +router.post('/deposit', assertRequestInput(CreateVEDeposit.validation), CreateVEDeposit.controller); +router.post('/increase', assertRequestInput(CreateVEIncrease.validation), CreateVEIncrease.controller); +router.post('/claim', assertRequestInput(CreateVEClaim.validation), CreateVEClaim.controller); +router.post('/withdraw', assertRequestInput(CreateVEWithdraw.validation), CreateVEWithdraw.controller); + +export default router; diff --git a/apps/api/src/app/controllers/ve/ve.test.ts b/apps/api/src/app/controllers/ve/ve.test.ts new file mode 100644 index 000000000..47cb78cb7 --- /dev/null +++ b/apps/api/src/app/controllers/ve/ve.test.ts @@ -0,0 +1,333 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { BigNumber, Contract, ethers } from 'ethers'; +import { ChainId } from '@thxnetwork/common/enums'; +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { sub, userWalletPrivateKey, widgetAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { WalletDocument } from '@thxnetwork/api/models/Wallet'; +import { signTxHash, timeTravel } from '@thxnetwork/api/util/jest/network'; +import { poll } from '@thxnetwork/api/util/polling'; +import SafeService from '@thxnetwork/api/services/SafeService'; + +const user = request.agent(app); +const { signer } = getProvider(ChainId.Hardhat); + +describe('VESytem', () => { + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + const amountInWei = String(ethers.utils.parseUnits('1000', 'ether')); + const chainId = ChainId.Hardhat; + + let safeWallet!: WalletDocument, + testBPT!: Contract, + testBPTGauge!: Contract, + testBAL!: Contract, + vethx!: Contract, + rdthx!: Contract, + rfthx!: Contract, + scthx!: Contract; + + it('Deploy Tokens', async () => { + safeWallet = await SafeService.findOne({ sub, poolId: { $exists: false }, safeVersion: { $exists: true } }); + expect(safeWallet.address).toBeDefined(); + + testBAL = new ethers.Contract(contractNetworks[chainId].BAL, contractArtifacts['BAL'].abi, signer); + testBPT = new ethers.Contract(contractNetworks[chainId].BPT, contractArtifacts['BPT'].abi, signer); + testBPTGauge = new ethers.Contract( + contractNetworks[chainId].BPTGauge, + contractArtifacts['BPTGauge'].abi, + signer, + ); + + vethx = new ethers.Contract( + contractNetworks[chainId].VotingEscrow, + contractArtifacts['VotingEscrow'].abi, + signer, + ); + rdthx = new ethers.Contract( + contractNetworks[chainId].RewardDistributor, + contractArtifacts['RewardDistributor'].abi, + signer, + ); + rfthx = new ethers.Contract( + contractNetworks[chainId].RewardFaucet, + contractArtifacts['RewardFaucet'].abi, + signer, + ); + scthx = new ethers.Contract( + contractNetworks[chainId].SmartWalletWhitelist, + contractArtifacts['SmartWalletWhitelist'].abi, + signer, + ); + }); + + describe('Create Reward Distribution', () => { + it('Create Reward Distribution after first week', async () => { + const amountBPT = String(ethers.utils.parseUnits('100000', 'ether')); + const amountBAL = String(ethers.utils.parseUnits('1000', 'ether')); + + // Travel past first week else this throws "Reward distribution has not started yet" + await timeTravel(60 * 60 * 24 * 7); + + // Deposit reward tokens into rdthx + await testBPT.approve(rfthx.address, amountBPT); + await testBAL.approve(rfthx.address, amountBAL); + await rfthx.depositEqualWeeksPeriod(testBPT.address, amountBPT, '4'); + await rfthx.depositEqualWeeksPeriod(testBAL.address, amountBAL, '4'); + }); + }); + + describe('Stake BPT ', () => { + it('Balance = total', async () => { + let tx = await testBPT.transfer(safeWallet.address, amountInWei); + tx = await tx.wait(); + const event = tx.events.find((ev) => ev.event === 'Transfer'); + expect(event).toBeDefined(); + const balanceInWei = await testBPT.balanceOf(safeWallet.address); + expect(balanceInWei.gt(0)).toBe(true); + }); + it('Approve', async () => { + const { status, body } = await user + .post('/v1/erc20/allowance') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ tokenAddress: testBPT.address, amountInWei, spender: testBPTGauge.address }); + expect(status).toBe(201); + + for (const tx of body) { + expect(tx.safeTxHash).toBeDefined(); + + const { signature } = await signTxHash(safeWallet.address, tx.safeTxHash, userWalletPrivateKey); + await user + .post('/v1/account/wallets/confirm') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash: tx.safeTxHash, signature }) + .expect(200); + } + }); + + it('Wait for approved amount', async () => { + // Replace with API call + await poll( + () => testBPT.allowance(safeWallet.address, testBPTGauge.address), + (result: BigNumber) => result.eq(0), + 1000, + ); + }); + + it('Stake 1000 BPT', async () => { + const { status, body } = await user + .post('/v1/liquidity/stake') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ amountInWei }); + expect(status).toBe(201); + for (const tx of body) { + expect(tx.safeTxHash).toBeDefined(); + + const { signature } = await signTxHash(safeWallet.address, tx.safeTxHash, userWalletPrivateKey); + await user + .post('/v1/account/wallets/confirm') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash: tx.safeTxHash, signature }) + .expect(200); + } + }); + + it('User received BPT-gauge', async () => { + await poll( + () => testBPTGauge.balanceOf(safeWallet.address), + (amount: BigNumber) => BigNumber.from(amount).eq(0), + 1000, + ); + + const balanceInWei = await testBPTGauge.balanceOf(safeWallet.address); + expect(balanceInWei.gt(0)).toBe(true); + }); + }); + + describe('Lock BPT-gauge ', () => { + it('WhiteList Safe Wallet', async () => { + // Move this to step 1 in VE UI modals + let tx = await scthx.approveWallet(safeWallet.address); + tx = await tx.wait(); + const event = tx.events.find((ev) => ev.event === 'ApproveWallet'); + expect(event).toBeDefined(); + }); + + it('Approve', async () => { + const { status, body } = await user + .post('/v1/erc20/allowance') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ + tokenAddress: contractNetworks[chainId].BPTGauge, + amountInWei, + spender: contractNetworks[chainId].VotingEscrow, + }); + expect(status).toBe(201); + + for (const tx of body) { + expect(tx.safeTxHash).toBeDefined(); + + const { signature } = await signTxHash(safeWallet.address, tx.safeTxHash, userWalletPrivateKey); + await user + .post('/v1/account/wallets/confirm') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash: tx.safeTxHash, signature }) + .expect(200); + } + }); + + it('Wait for approved amount', async () => { + // Replace with API call + await poll( + () => testBPTGauge.allowance(safeWallet.address, vethx.address), + (result: BigNumber) => result.eq(0), + 1000, + ); + }); + + it('Deposit 1000', async () => { + const lockEndTimestamp = Math.ceil(Date.now() / 1000) + 60 * 60 * 24 * 7 * 12; // 12 weeks from now + const { status, body } = await user + .post('/v1/ve/deposit') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ amountInWei, lockEndTimestamp }); + expect(status).toBe(201); + for (const tx of body) { + expect(tx.safeTxHash).toBeDefined(); + + const { signature } = await signTxHash(safeWallet.address, tx.safeTxHash, userWalletPrivateKey); + await user + .post('/v1/account/wallets/confirm') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash: tx.safeTxHash, signature }) + .expect(200); + } + }); + + it('Balance = total - deposit', async () => { + await poll( + () => vethx.locked(safeWallet.address), + (result: { amount: BigNumber }) => BigNumber.from(result.amount).eq(0), + 1000, + ); + + const balanceInWei = await testBPTGauge.balanceOf(safeWallet.address); + const totalMinDeposit = BigNumber.from(amountInWei).sub(amountInWei); + + expect(balanceInWei.eq(totalMinDeposit)).toBe(true); + }); + + it('List locks ', async () => { + const { status, body } = await user + .get('/v1/ve') + .query({ walletId: String(safeWallet._id) }) + .set({ Authorization: widgetAccessToken }) + .send(); + + expect(Number(body[0].rewards)).toBe; + expect(Number(body[0].end)).toBeGreaterThan(Number(body[0].now)); + expect(body[0].amount).toBe(amountInWei); + expect(status).toBe(200); + }); + }); + + // describe('Claim THX incentives', () => { + // it('Claim Tokens (after 14 days)', async () => { + // console.log(await rfthx.getUpcomingRewardsForNWeeks(testBPT.address, 4)); + // console.log(await rfthx.getUpcomingRewardsForNWeeks(testBAL.address, 4)); + + // // Travel past end date of the first reward eligible week + // await timeTravel(60 * 60 * 24 * 8); + + // console.log(await rfthx.getUpcomingRewardsForNWeeks(testBPT.address, 4)); + // console.log(await rfthx.getUpcomingRewardsForNWeeks(testBAL.address, 4)); + + // const balance = await testBPT.balanceOf(safeWallet.address); + // expect(balance).toBeDefined(); + + // let tx = await rdthx.claimToken(safeWallet.address, testBPT.address); + // tx = await tx.wait(); + + // const event = tx.events.find((ev) => ev.event === 'TokenCheckpointed'); + // expect(event).toBeDefined(); + + // const balanceAfterClaim = await testBPT.balanceOf(safeWallet.address); + // expect(BigNumber.from(balance).lt(balanceAfterClaim)).toBe(true); + // }); + // }); + + describe('Withdraw BPT', () => { + it('Withdraw', async () => { + const { status, body } = await user + .post('/v1/ve/withdraw') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ isEarlyAttempt: false }); + expect(status).toBe(403); + console.log(body); + expect(body.error.message).toBe('Funds are locked'); + }); + + it('Withdraw Early 1000 - penalty', async () => { + const { status, body } = await user + .post('/v1/ve/withdraw') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ isEarlyAttempt: true }); + expect(status).toBe(201); + for (const tx of body) { + expect(tx.safeTxHash).toBeDefined(); + + const { signature } = await signTxHash(safeWallet.address, tx.safeTxHash, userWalletPrivateKey); + await user + .post('/v1/account/wallets/confirm') + .set({ Authorization: widgetAccessToken }) + .query({ walletId: String(safeWallet._id) }) + .send({ chainId: ChainId.Hardhat, safeTxHash: tx.safeTxHash, signature }) + .expect(200); + } + }); + + // it('Balance = total + deposit - penalty', async () => { + // await poll( + // () => vethx.locked(safeWallet.address), + // (result: { amount: BigNumber }) => !BigNumber.from(result.amount).eq(0), + // 1000, + // ); + + // const balanceInWei = await testBPT.balanceOf(safeWallet.address); + // // const rdBalanceInWei = await testBPT.balanceOf(rdthx.address); + // // console.log( + // // String(balanceInWei), // 1024259542566872427984000 + // // String(rdBalanceInWei), // 25740457433127572016000 + // // String(balanceInWei.add(rdBalanceInWei)), // 1050000000000000000000000 + // // String(balanceInWei.add(rdBalanceInWei).sub(totalSupplyInWei)), // 50000000000000000000000 + // // ); + + // // Due to early exit expect less BPT to be returned and the balance of + // // the penalty treasury to increase. Formula to calculate the penalty is in + // // the VE contract. + // // Eg: + // // balanceInWei = 999917384259259259260000 + // // rdBalanceInWei = 82615740740740740000 + // // console.log(String(balanceInWei), amountInWei); + // // Should be larger due to reward claim + // // console.log(String(balanceInWei)); + // // console.log(String(amountInWei)); + // // console.log(String(BigNumber.from(balanceInWei).sub(BigNumber.from(amountInWei)))); + // console.log('balanceInWei', balanceInWei.toString()); + // expect(BigNumber.from(balanceInWei).gt(BigNumber.from(amountInWei))).toBe(true); + // }); + }); +}); diff --git a/apps/api/src/app/controllers/ve/withdraw/post.controller.ts b/apps/api/src/app/controllers/ve/withdraw/post.controller.ts new file mode 100644 index 000000000..4321bc886 --- /dev/null +++ b/apps/api/src/app/controllers/ve/withdraw/post.controller.ts @@ -0,0 +1,36 @@ +import { Request, Response } from 'express'; +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { body, query } from 'express-validator'; +import VoteEscrowService from '@thxnetwork/api/services/VoteEscrowService'; + +const validation = [ + query('walletId').isMongoId(), + body('isEarlyAttempt') + .isBoolean() + .customSanitizer((val: string) => (val ? JSON.parse(val) : false)), +]; + +const controller = async ({ wallet, body }: Request, res: Response) => { + // Check sufficient BPT approval + const { web3 } = getProvider(); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + const lock = await ve.methods.locked(wallet.address).call(); + const now = (await web3.eth.getBlock('latest')).timestamp; + + // Check if client requests early exit and end date has not past + const isEarlyWithdraw = Number(lock.end) > Number(now); + if (!body.isEarlyAttempt && isEarlyWithdraw) { + throw new ForbiddenError('Funds are locked'); + } + + // Propose the withdraw transaction + const txs = await VoteEscrowService.withdraw(wallet, isEarlyWithdraw); + + res.status(201).json(txs); +}; +export { controller, validation }; diff --git a/apps/api/src/app/controllers/webhook/daily/daily-quest-webhook.test.ts b/apps/api/src/app/controllers/webhook/daily/daily-quest-webhook.test.ts new file mode 100644 index 000000000..743c384c7 --- /dev/null +++ b/apps/api/src/app/controllers/webhook/daily/daily-quest-webhook.test.ts @@ -0,0 +1,95 @@ +import request from 'supertest'; +import app from '@thxnetwork/api/'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import { account4, dashboardAccessToken, widgetAccessToken4 } from '@thxnetwork/api/util/jest/constants'; +import { isAddress } from 'web3-utils'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { QuestDailyDocument } from '@thxnetwork/api/models'; +import { poll } from '@thxnetwork/api/util/polling'; +import { Job } from '@thxnetwork/api/models/Job'; +import { v4 } from 'uuid'; + +const user = request.agent(app); + +describe('Daily Rewards WebHooks', () => { + let poolId: string, dailyReward: QuestDailyDocument; + const eventName = v4(); + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + it('POST /pools', (done) => { + user.post('/v1/pools') + .set('Authorization', dashboardAccessToken) + .send() + .expect((res: request.Response) => { + expect(isAddress(res.body.safeAddress)).toBe(true); + poolId = res.body._id; + }) + .expect(201, done); + }); + + it('POST /daily-rewards', (done) => { + user.post(`/v1/pools/${poolId}/quests/${QuestVariant.Daily}`) + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .send({ + isPublished: true, + variant: QuestVariant.Daily, + title: 'Expiration date is next 30 min', + description: 'Lorem ipsum dolor sit amet', + amounts: JSON.stringify([100]), + eventName, + index: 0, + }) + .expect(async ({ body }: request.Response) => { + expect(body.uuid).toBeDefined(); + expect(body.eventName).toBeDefined(); + expect(body.amounts[0]).toBe(100); + dailyReward = body; + }) + .expect(201, done); + }); + + it('POST /webhook/daily/:uuid', async () => { + const { status } = await user.post(`/v1/webhook/daily/${eventName}`).send({ + address: account4.address, + }); + expect(status).toBe(201); + }); + + it('GET /participant to update identity', async () => { + const { status } = await user + .get(`/v1/participants`) + .query({ poolId }) + .set({ Authorization: widgetAccessToken4 }); + expect(status).toBe(200); + }); + + it('POST /quests/daily/:id/entries', async () => { + const { status, body } = await user + .post(`/v1/quests/daily/${dailyReward._id}/entries`) + .set({ 'X-PoolId': poolId, 'Authorization': widgetAccessToken4 }) + .send({ recaptcha: 'test' }); + expect(body.jobId).toBeDefined(); + expect(status).toBe(200); + + await poll( + () => Job.findById(body.jobId), + (job: any) => !job.lastRunAt, + 1000, + ); + + const job = await Job.findById(body.jobId); + expect(job.lastRunAt).toBeDefined(); + }); + + it('POST /quests/daily/:id/entries should throw an error', (done) => { + user.post(`/v1/quests/daily/${dailyReward._id}/entries`) + .set({ 'X-PoolId': poolId, 'Authorization': widgetAccessToken4 }) + .send({ recaptcha: 'test' }) + .expect(({ body }: request.Response) => { + expect(body.error).toBe('You have completed this quest within the last 24 hours.'); + }) + .expect(200, done); + }); +}); diff --git a/apps/api/src/app/controllers/webhook/daily/post.controller.ts b/apps/api/src/app/controllers/webhook/daily/post.controller.ts new file mode 100644 index 000000000..7813e179b --- /dev/null +++ b/apps/api/src/app/controllers/webhook/daily/post.controller.ts @@ -0,0 +1,34 @@ +import { BadRequestError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { getIdentityForAddress, getIdentityForCode } from '../milestones/claim/post.controller'; +import { Event } from '@thxnetwork/api/models/Event'; +import { Pool, QuestDaily } from '@thxnetwork/api/models'; + +const validation = [ + param('uuid').isUUID('4'), + body('code').optional().isUUID(4), + body('address').optional().isEthereumAddress(), +]; + +const controller = async (req: Request, res: Response) => { + const quest = await QuestDaily.findOne({ eventName: req.params.uuid }); + if (!quest) throw new NotFoundError('Could not find a daily reward for this token'); + + const pool = await Pool.findById(quest.poolId); + if (!pool) throw new NotFoundError('Could not find a campaign pool for this reward.'); + + if (!req.body.code && !req.body.address) { + throw new BadRequestError('This request requires either a wallet code or address'); + } + + const identity = req.body.code + ? await getIdentityForCode(pool, req.body.code) + : await getIdentityForAddress(pool, req.body.address); + + await Event.create({ name: quest.eventName, identityId: identity._id, poolId: pool._id }); + + res.status(201).end(); +}; + +export { validation, controller }; diff --git a/apps/api/src/app/controllers/webhook/gateway/post.controller.ts b/apps/api/src/app/controllers/webhook/gateway/post.controller.ts new file mode 100644 index 000000000..8ef66b167 --- /dev/null +++ b/apps/api/src/app/controllers/webhook/gateway/post.controller.ts @@ -0,0 +1,68 @@ +import crypto from 'crypto'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { WEBHOOK_SIGNING_SECRET } from '@thxnetwork/api/config/secrets'; +import { Wallet } from '@thxnetwork/api/models'; +import { logger } from '@thxnetwork/api/util/logger'; +import { formatUnits } from 'ethers/lib/utils'; +import VoteEscrowService from '@thxnetwork/api/services/VoteEscrowService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import BalancerService from '@thxnetwork/api/services/BalancerService'; + +const validation = [body('payload').isString(), body('signature').isString()]; + +// Helper method to verify payload signature +function constructEvent(payload, signature, secret) { + const hmac = crypto.createHmac('sha256', secret); + hmac.update(payload); + const calculatedSignature = hmac.digest('base64'); + if (signature !== calculatedSignature) throw new Error('Failed signature verification'); + return JSON.parse(payload); +} + +const controller = async (req: Request, res: Response) => { + let result = false; + try { + // Verifies and parses the payload using the WEBHOOK_SIGNING_SECRET which you can get in Developer -> Webhooks + const event = constructEvent(req.body.payload, req.body.signature, WEBHOOK_SIGNING_SECRET); + + switch (event.type) { + case 'quest_entry.create': { + const { identities, metadata } = event; + if (!identities.length) throw new Error('No identities found in the event'); + + const account = await AccountProxy.getByIdentity(identities[0]); + if (!account) throw new Error('No account found for the identity'); + + const wallets = await Wallet.find({ + sub: account.sub, + chainId: { $exists: true }, + address: { $exists: true }, + }); + if (!wallets.length) throw new Error('No wallets found for the account'); + + // Get largest lock and validate with provided metadata + const promises = wallets.map(async (wallet) => await VoteEscrowService.list(wallet)); + const locks = await Promise.all(promises); + const [largestLock] = locks.sort((a, b) => b.amount - a.amount); + const lockAmount = formatUnits(largestLock.amount, 18); + const bptPrice = BalancerService.pricing['20USDC-80THX']; + const largestAmountInUSD = Number(lockAmount) * bptPrice; + + result = largestAmountInUSD >= Number(metadata); + + break; + } + default: { + console.log('Unhandled event type ' + event.type); + } + } + + return res.json({ result }); + } catch (error) { + logger.error(error.message); + return res.status(400).send('Webhook Error: ' + error.message); + } +}; + +export { validation, controller }; diff --git a/apps/api/src/app/controllers/webhook/milestones/claim/post.controller.ts b/apps/api/src/app/controllers/webhook/milestones/claim/post.controller.ts new file mode 100644 index 000000000..2ca826363 --- /dev/null +++ b/apps/api/src/app/controllers/webhook/milestones/claim/post.controller.ts @@ -0,0 +1,49 @@ +import { Request, Response } from 'express'; +import { BadRequestError, NotFoundError } from '@thxnetwork/api/util/errors'; +import { body, param } from 'express-validator'; +import { toChecksumAddress } from 'web3-utils'; +import { Pool, PoolDocument, Identity, Event, QuestCustom } from '@thxnetwork/api/models'; +import IdentityService from '@thxnetwork/api/services/IdentityService'; + +const validation = [ + param('uuid').isUUID('4'), + body('code').optional().isUUID(4), + body('address') + .optional() + .isEthereumAddress() + .customSanitizer((address) => toChecksumAddress(address)), +]; + +const controller = async (req: Request, res: Response) => { + const customQuest = await QuestCustom.findOne({ uuid: req.params.uuid }); + if (!customQuest) throw new NotFoundError('Could not find a milestone reward for this token'); + + const pool = await Pool.findById(customQuest.poolId); + if (!pool) throw new NotFoundError('Could not find a campaign pool for this reward.'); + + if (!req.body.code && !req.body.address) { + throw new BadRequestError('This request requires either a wallet code or address'); + } + + const identity = req.body.code + ? await getIdentityForCode(pool, req.body.code) + : await getIdentityForAddress(pool, req.body.address); + + await Event.create({ name: customQuest.eventName, identityId: identity._id, poolId: pool._id }); + + res.status(201).end(); +}; + +export function getIdentityForCode(pool: PoolDocument, code: string) { + return Identity.findOne({ poolId: pool._id, uuid: code }); +} + +// @peterpolman (FK still depends on this) +// This function should deprecate as soon as clients implement the wallet onboarding webhook +// Defaulting into identity derivation for the provided address. This will require FK to present derived +// identity uuids in their client in order to connect the identity to their account. +export function getIdentityForAddress(pool: PoolDocument, address: string) { + return IdentityService.getIdentityForSalt(pool, address); +} + +export { validation, controller }; diff --git a/apps/api/src/app/controllers/webhook/webhook.router.ts b/apps/api/src/app/controllers/webhook/webhook.router.ts new file mode 100644 index 000000000..39fe2e0a8 --- /dev/null +++ b/apps/api/src/app/controllers/webhook/webhook.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as MilestoneReward from './milestones/claim/post.controller'; +import * as DailyReward from './daily/post.controller'; +import * as CreateController from './gateway/post.controller'; + +const router: express.Router = express.Router(); + +// Custom webhooks for Webhook Quest consumption +router.post('/gateway', assertRequestInput(CreateController.validation), CreateController.controller); + +// Deprecate soon +router.post('/milestone/:uuid/claim', assertRequestInput(MilestoneReward.validation), MilestoneReward.controller); +router.post('/daily/:uuid', assertRequestInput(DailyReward.validation), DailyReward.controller); + +export default router; diff --git a/apps/api/src/app/controllers/webhooks/delete.controller.ts b/apps/api/src/app/controllers/webhooks/delete.controller.ts new file mode 100644 index 000000000..5d0095c36 --- /dev/null +++ b/apps/api/src/app/controllers/webhooks/delete.controller.ts @@ -0,0 +1,12 @@ +import { param } from 'express-validator'; +import { Request, Response } from 'express'; +import { Webhook } from '@thxnetwork/api/models/Webhook'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + await Webhook.findByIdAndDelete(req.params.id); + res.status(204).end(); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/webhooks/list.controller.ts b/apps/api/src/app/controllers/webhooks/list.controller.ts new file mode 100644 index 000000000..5eaa09fab --- /dev/null +++ b/apps/api/src/app/controllers/webhooks/list.controller.ts @@ -0,0 +1,25 @@ +import { Request, Response } from 'express'; +import { Webhook, WebhookDocument } from '@thxnetwork/api/models/Webhook'; +import { WebhookRequest } from '@thxnetwork/api/models/WebhookRequest'; + +const validation = []; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Webhooks'] + const poolId = req.header('x-poolid'); + const webhooks = await Webhook.find({ poolId }); + const response = await Promise.all( + webhooks.map(async (webhook: WebhookDocument) => { + const webhookRequests = await WebhookRequest.find({ webhookId: String(webhook._id) }) + .sort({ createdAt: -1 }) + .limit(50); + return { + ...webhook.toJSON(), + webhookRequests, + }; + }), + ); + res.json(response); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/webhooks/patch.controller.ts b/apps/api/src/app/controllers/webhooks/patch.controller.ts new file mode 100644 index 000000000..fc72b93ac --- /dev/null +++ b/apps/api/src/app/controllers/webhooks/patch.controller.ts @@ -0,0 +1,13 @@ +import { body, param } from 'express-validator'; +import { Request, Response } from 'express'; +import { Webhook } from '@thxnetwork/api/models/Webhook'; + +const validation = [param('id').isMongoId(), body('url').isURL({ require_tld: false })]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Webhooks'] + const webhook = await Webhook.findByIdAndUpdate(req.params.id, { url: req.body.url }, { new: true }); + res.json(webhook); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/webhooks/post.controller.ts b/apps/api/src/app/controllers/webhooks/post.controller.ts new file mode 100644 index 000000000..7375b5210 --- /dev/null +++ b/apps/api/src/app/controllers/webhooks/post.controller.ts @@ -0,0 +1,17 @@ +import { body } from 'express-validator'; +import { Request, Response } from 'express'; +import { Webhook } from '@thxnetwork/api/models/Webhook'; + +const validation = [body('url').isURL({ require_tld: false })]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Webhooks'] + const webhook = await Webhook.create({ + poolId: req.header('x-poolid'), + url: req.body.url, + }); + + res.status(201).json({ ...webhook.toJSON(), webhookRequests: [] }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/webhooks/webhooks.router.ts b/apps/api/src/app/controllers/webhooks/webhooks.router.ts new file mode 100644 index 000000000..2398c8be5 --- /dev/null +++ b/apps/api/src/app/controllers/webhooks/webhooks.router.ts @@ -0,0 +1,39 @@ +import express, { Router } from 'express'; +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import * as ListWebhook from './list.controller'; +import * as PatchWebhook from './patch.controller'; +import * as CreateWebhook from './post.controller'; +import * as DeleteWebhook from './delete.controller'; + +const router: express.Router = express.Router(); + +router.get( + '/', + guard.check(['webhooks:read']), + assertPoolAccess, + assertRequestInput(ListWebhook.validation), + ListWebhook.controller, +); +router.patch( + '/:id', + guard.check(['webhooks:read']), + assertPoolAccess, + assertRequestInput(PatchWebhook.validation), + PatchWebhook.controller, +); +router.post( + '/', + guard.check(['webhooks:write', 'webhooks:read']), + assertPoolAccess, + assertRequestInput(CreateWebhook.validation), + CreateWebhook.controller, +); +router.delete( + '/:id', + guard.check(['webhooks:write', 'webhooks:read']), + assertPoolAccess, + assertRequestInput(DeleteWebhook.validation), + DeleteWebhook.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/widget/get.controller.ts b/apps/api/src/app/controllers/widget/get.controller.ts new file mode 100644 index 000000000..0807ff26b --- /dev/null +++ b/apps/api/src/app/controllers/widget/get.controller.ts @@ -0,0 +1,31 @@ +import { AUTH_URL } from '@thxnetwork/api/config/secrets'; +import { Brand, Pool, Widget } from '@thxnetwork/api/models'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { param } from 'express-validator'; + +const validation = [param('id').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + const widget = await Widget.findOne({ poolId: req.params.id }); + if (!widget) throw new NotFoundError('Widget not found'); + + const pool = await Pool.findById(req.params.id); + if (!pool) throw new NotFoundError('Pool not found'); + + const brand = await Brand.findOne({ poolId: req.params.id }); + + res.json({ + title: pool.settings.title, + description: pool.settings.description, + logoUrl: brand ? brand.logoImgUrl : AUTH_URL + '/img/logo-padding.png', + backgroundUrl: brand ? brand.backgroundImgUrl : '', + theme: widget.theme, + domain: widget.domain, + chainId: pool.chainId, + poolId: pool._id, + slug: pool.settings.slug || pool._id, + }); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/widget/js/get.controller.ts b/apps/api/src/app/controllers/widget/js/get.controller.ts new file mode 100644 index 000000000..8d932ae4b --- /dev/null +++ b/apps/api/src/app/controllers/widget/js/get.controller.ts @@ -0,0 +1,566 @@ +import { API_URL, AUTH_URL, DASHBOARD_URL, NODE_ENV, WIDGET_URL } from '@thxnetwork/api/config/secrets'; +import BrandService from '@thxnetwork/api/services/BrandService'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { QuestInvite } from '@thxnetwork/api/models/QuestInvite'; +import { Widget } from '@thxnetwork/api/models/Widget'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; +import { Request, Response } from 'express'; +import { query, param } from 'express-validator'; +import { minify } from 'terser'; + +const validation = [ + param('id').isMongoId(), + query('identity').optional().isUUID(), + query('containerSelector').optional().isString(), +]; + +const controller = async (req: Request, res: Response) => { + // #swagger.tags = ['Widget'] + const referralRewards = await QuestInvite.find({ + poolId: req.params.id, + }); + const refs = JSON.stringify( + referralRewards + .filter((r) => r.successUrl) + .map((r) => { + return { + uuid: r.uuid, + successUrl: r.successUrl, + }; + }), + ); + + const pool = await PoolService.getById(req.params.id); + if (!pool) throw new NotFoundError('Pool not found.'); + + const expired = pool.settings.endDate ? pool.settings.endDate.getTime() <= Date.now() : false; + const brand = await BrandService.get(pool._id); + const widget = await Widget.findOne({ poolId: req.params.id }); + const referrerHeader = req.header('Referrer'); + const widgetOrigin = widget.domain ? new URL(widget.domain).origin : ''; + const origin = referrerHeader ? new URL(referrerHeader).origin : ''; + + // Set active to true if there is a request made from the configured domain + if (widgetOrigin === origin && !widget.active) { + await widget.updateOne({ active: true }); + } + + const data = ` +if (typeof window.THXWidget !== 'undefined') { + window.THXWidget.onLoad(); +} else { + class THXWidget { + MD_BREAKPOINT = 990; + public isAuthenticated = false; + + constructor(settings) { + this.settings = settings; + this.theme = JSON.parse(settings.theme); + this.init(); + } + + get defaultStyles() { + const isCustomContainer = !!this.settings.containerSelector; + return { + sm: { + width: '100%', + height: '100%', + maxHeight: 'none', + top: 0, + left: 0, + right: 0, + bottom: 0, + border: 0, + borderRadius: 0, + }, + md: { + top: 'auto', + bottom: isCustomContainer ? 'auto' : '100px', + maxHeight: isCustomContainer ? 'none' : '703px', + width: isCustomContainer ? '100%' : '400px', + border: 0, + borderRadius: isCustomContainer ? '0px' : '10px', + height: isCustomContainer ? '100%' : 'calc(100% - 115px)', + }, + } + } + + init() { + const waitForBody = () => new Promise((resolve) => { + const tick = () => { + if (document.getElementsByTagName('body').length) { + clearInterval(timer) + resolve() + } + } + const timer = setInterval(tick, 1000); + }); + waitForBody().then(this.onLoad.bind(this)); + } + + public setIdentity(identity) { + this.iframe.contentWindow.postMessage({ message: 'thx.auth.identity', identity }, this.settings.widgetUrl); + } + + public signin() { + this.iframe.contentWindow.postMessage({ message: 'thx.auth.signin' }, this.settings.widgetUrl); + } + + public signout() { + this.iframe.contentWindow.postMessage({ message: 'thx.auth.signout' }, this.settings.widgetUrl); + } + + public open(widgetPath) { + if (!widgetPath) return; + + const { widgetUrl, poolId, chainId, theme } = this.settings; + const path = '/c/' + poolId + widgetPath; + const isMobile = window.matchMedia('(pointer:coarse)').matches; + + if (isMobile) { + // Window _blank will be blocked by mobile OS so we redirect the current window + window.location.href = this.settings.widgetUrl + path + } else { + this.iframe.contentWindow.postMessage({ message: 'thx.iframe.navigate', path }, widgetUrl); + this.show(true); + } + } + + public connect(uuid) { + this.open('/w/' + uuid); + } + + public quests = { + list: () => { + this.iframe.contentWindow.postMessage({ message: 'thx.quests.list' }, this.settings.widgetUrl); + } + } + + onLoad() { + this.referrals = JSON.parse(this.settings.refs).filter((r) => r.successUrl); + this.iframe = this.createIframe(); + + if (this.settings.isPublished) { + this.notifications = this.createNotifications(0); + this.message = this.createMessage(); + this.launcher = this.settings.cssSelector ? this.selectLauncher() : this.createLauncher(); + this.container = this.createContainer(this.iframe, this.launcher, this.message); + } + + this.parseURL(); + + window.matchMedia('(max-width: 990px)').addListener(this.onMatchMedia.bind(this)); + window.onmessage = this.onMessage.bind(this); + } + + parseURL() { + const url = new URL(window.location.href) + this.ref = url.searchParams.get('ref'); + if (!this.ref) return; + + this.successUrls = this.referrals.map((r) => r.successUrl); + if (!this.successUrls.length) return; + } + + get isSmallMedia() { + const getWidth = () => window.innerWidth; + return getWidth() < this.MD_BREAKPOINT; + } + + createURL() { + const parentUrl = new URL(window.location.href) + const path = parentUrl.searchParams.get('thx_widget_path'); + const { widgetUrl, poolId, chainId, theme, expired, logoUrl, backgroundUrl, title } = this.settings; + const url = new URL(widgetUrl); + + url.pathname = this.widgetPath = '/c/' + poolId + (path || '/quests'); + url.searchParams.append('origin', window.location.origin); + + return url; + } + + createIframe() { + const { widgetUrl, poolId, chainId, theme, align, expired, containerSelector } = this.settings; + const iframe = document.createElement('iframe'); + const styles = this.isSmallMedia ? this.defaultStyles['sm'] : this.defaultStyles['md']; + const url = this.createURL(); + + iframe.id = 'thx-iframe'; + iframe.src = url; + iframe.setAttribute('data-hj-allow-iframe', true); + + if (containerSelector) { + Object.assign(iframe.style, this.defaultStyles[this.isSmallMedia ? 'sm' : 'md']); + return iframe; + } + + let top, bottom, left, right, marginLeft, marginTop, transformOrigin; + + if (!this.isSmallMedia) { + switch(align) { + case 'left' : + top = 'auto'; + bottom = !this.settings.cssSelector ? '100px' : '15px'; + left = '15px'; + right = 'auto'; + transformOrigin = 'bottom left'; + break; + case 'right' : + top = 'auto'; + bottom = !this.settings.cssSelector ? '100px' : '15px'; + left = 'auto'; + right = '15px'; + transformOrigin = 'bottom right'; + break; + case 'center' : + top = '50%'; + left = '50%'; + right = 'auto'; + bottom = 'auto'; + marginLeft = '-200px'; + marginTop = '-340px'; + transformOrigin = 'center center'; + break; + } + } else { + top = '0'; + bottom = '0'; + left = '0'; + right = '0'; + } + + Object.assign(iframe.style, { + ...styles, + zIndex: 99999999, + display: 'flex', + top, + bottom, + left, + right, + marginLeft, + marginTop, + position: 'fixed', + border: '0', + opacity: '0', + boxShadow: 'rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px', + transform: 'scale(0)', + transformOrigin, + transition: '.2s opacity ease, .1s transform ease', + }); + + return iframe; + } + + createNotifications(counter) { + const notifications = document.createElement('div'); + notifications.id = 'thx-notifications'; + Object.assign(notifications.style, { + display: 'none', + fontFamily: 'Arial', + fontSize: '13px', + justifyContent: 'center', + alignItems: 'center', + width: '20px', + height: '20px', + color: '#FFFFFF', + position: 'absolute', + backgroundColor: '#CA0000', + borderRadius: '50%', + userSelect: 'none', + }); + notifications.innerHTML = counter; + return notifications; + } + + createMessage() { + const { message, logoUrl, align } = this.settings; + const messageBox = document.createElement('div'); + const closeBox = document.createElement('button'); + + messageBox.id = 'thx-message'; + + closeBox.innerHTML = '×'; + + Object.assign(closeBox.style, { + display: 'flex', + fontFamily: 'Arial', + fontSize: '16px', + justifyContent: 'center', + alignItems: 'center', + width: '20px', + height: '20px', + border: '0', + color: '#000000', + position: 'absolute', + backgroundColor: 'transparent', + top: '0', + right: '0', + opacity: '0.5', + transform: 'scale(.9)', + transition: '.2s opacity ease, .1s transform ease', + }); + closeBox.addEventListener('mouseenter', () => { + closeBox.style.opacity = '1'; + closeBox.style.transform = 'scale(1)'; + }); + closeBox.addEventListener('mouseleave', () => { + closeBox.style.opacity = '.5'; + closeBox.style.transform = 'scale(.9)'; + }); + closeBox.addEventListener('click', () => { + this.message.remove(); + }); + + Object.assign(messageBox.style, { + zIndex: 9999999, + display: message ? 'flex' : 'none', + lineHeight: 1.5, + fontFamily: 'inherit, sans-serif', + fontSize: '12px', + fontWeight: 'normal', + justifyContent: 'center', + alignItems: 'center', + width: '200px', + color: '#000000', + position: 'fixed', + backgroundColor: '#FFFFFF', + borderRadius: '5px', + userSelect: 'none', + padding: '10px 10px 10px', + bottom: '90px', + right: align === 'right' ? '15px' : 'auto', + left: align === 'left' ? '15px' : 'auto', + boxShadow: 'rgb(50 50 93 / 25%) 0px 50px 100px -20px, rgb(0 0 0 / 30%) 0px 30px 60px -30px', + opacity: 0, + transform: 'scale(0)', + transition: '.2s opacity ease, .1s transform ease', + }); + + const wrapper = document.createElement('span'); + wrapper.style.zIndex = 0; + wrapper.innerHTML = message; + messageBox.appendChild(wrapper); + messageBox.appendChild(closeBox); + + return messageBox; + } + + selectLauncher() { + const launcher = document.querySelector(this.settings.cssSelector); + if (!launcher) { + console.error("THX widget can't find the launcher for selector: " + this.settings.cssSelector); + return; + } + + launcher.addEventListener('click', this.onClickLauncher.bind(this)); + + setTimeout(() => { + const url = new URL(window.location.href) + const widgetPath = url.searchParams.get('thx_widget_path'); + + this.show(!!widgetPath); + }, 350); + + return launcher; + } + + createLauncher() { + const svgGift = this.settings.iconImg + ? '<img id="thx-svg-icon" style="display:block; margin: auto;" width="40" height="40" src="' + this.settings.iconImg + '" alt="Widget launcher icon" />' + : '<svg id="thx-svg-icon" style="display:block; margin: auto; fill: '+this.theme.elements.launcherIcon.color+'; width: 20px; height: 20px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M32 448c0 17.7 14.3 32 32 32h160V320H32v128zm256 32h160c17.7 0 32-14.3 32-32V320H288v160zm192-320h-42.1c6.2-12.1 10.1-25.5 10.1-40 0-48.5-39.5-88-88-88-41.6 0-68.5 21.3-103 68.3-34.5-47-61.4-68.3-103-68.3-48.5 0-88 39.5-88 88 0 14.5 3.8 27.9 10.1 40H32c-17.7 0-32 14.3-32 32v80c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-80c0-17.7-14.3-32-32-32zm-326.1 0c-22.1 0-40-17.9-40-40s17.9-40 40-40c19.9 0 34.6 3.3 86.1 80h-86.1zm206.1 0h-86.1c51.4-76.5 65.7-80 86.1-80 22.1 0 40 17.9 40 40s-17.9 40-40 40z"/></svg>'; + const launcher = document.createElement('div'); + launcher.id = 'thx-launcher'; + + Object.assign(launcher.style, { + zIndex: 9999999, + display: 'flex', + width: '60px', + height: '60px', + backgroundColor: this.theme.elements.launcherBg.color, + borderRadius: '50%', + cursor: 'pointer', + position: 'fixed', + bottom: '15px', + right: !this.settings.cssSelector ? this.settings.align === 'right' ? '15px' : 'auto' : 'auto', + left: !this.settings.cssSelector ? this.settings.align === 'left' ? '15px' : 'auto' : 'auto', + opacity: 0, + transition: '.2s opacity ease, .1s transform ease', + }); + + launcher.innerHTML = svgGift; + launcher.addEventListener('click', this.onClickLauncher.bind(this)); + launcher.appendChild(this.notifications); + + setTimeout(() => { + launcher.style.opacity = 1; + launcher.style.transform = 'scale(1)'; + + this.message.style.opacity = 1; + this.message.style.transform = 'scale(1)'; + + const url = new URL(window.location.href) + const widgetPath = url.searchParams.get('thx_widget_path'); + this.show(!!widgetPath) + }, 350); + + return launcher; + } + + createContainer(iframe, launcher, message) { + const { containerSelector, cssSelector } = this.settings; + let container; + if (containerSelector) { + container = document.querySelector(containerSelector) + if (!container) throw new Error("Could not find an HTML element for selector: '" + containerSelector + "'.") + container.appendChild(iframe); + } else { + container = document.createElement('div'); + container.id = 'thx-container'; + container.appendChild(iframe); + + if (!cssSelector) { + container.appendChild(launcher); + container.appendChild(message); + } + + document.body.appendChild(container); + } + return container; + } + + storeRef(ref) { + if (!ref) return; + + window.localStorage.setItem('thx:widget:' + this.settings.poolId + ':ref', ref); + this.iframe.contentWindow.postMessage({ message: 'thx.config.ref', ref }, this.settings.widgetUrl); + this.timer = window.setInterval(this.onURLDetectionCallback.bind(this), 500); + } + + onMessage(event) { + if (event.origin !== this.settings.widgetUrl) return; + const { message, amount, isAuthenticated, url } = event.data; + switch (message) { + case 'thx.auth.signin': { + this.onSignin(url); + break + } + case 'thx.widget.ready': { + this.onWidgetReady(); + break + } + case 'thx.reward.amount': { + this.notifications.innerText = amount; + this.notifications.style.display = amount ? 'flex' : 'none'; + break; + } + case 'thx.widget.toggle': { + this.show(!Number(this.iframe.style.opacity)); + break; + } + case 'thx.auth.status': { + this.isAuthenticated = isAuthenticated; + break; + } + } + } + + onSignin(url) { + window.open(url, '_blank'); + } + + onClickLauncher() { + const isMobile = window.matchMedia('(pointer:coarse)').matches; + if (window.ethereum && isMobile) { + const deeplink = 'https://metamask.app.link/dapp/'; + const ua = navigator.userAgent.toLowerCase(); + const isAndroid = ua.indexOf("android") > -1; + const url = isAndroid ? deeplink + this.createURL() : this.createURL(); + window.open(url, '_blank'); + } else if (!window.ethereum && isMobile) { + window.open(this.createURL(), '_blank'); + } else { + this.show(!Number(this.iframe.style.opacity)); + } + this.message.remove(); + } + + onWidgetReady() { + const parentUrl = new URL(window.location.href) + const widgetPath = parentUrl.searchParams.get('thx_widget_path'); + + this.open(widgetPath); + this.storeRef(this.ref); + + if (this.settings.identity) { + this.setIdentity(this.settings.identity) + } + } + + show(isShown) { + const { containerSelector } = this.settings; + const shouldShow = isShown || !!containerSelector; + + this.iframe.style.opacity = shouldShow ? '1' : '0'; + this.iframe.style.transform = shouldShow ? 'scale(1)' : 'scale(0)'; + + if (this.iframe.contentWindow) { + this.iframe.contentWindow.postMessage({ message: 'thx.iframe.show', shouldShow }, this.settings.widgetUrl); + } + + if (shouldShow) this.message.remove(); + } + + onURLDetectionCallback() { + for (const ref of this.referrals) { + if (!(this.successUrls.filter((url) => url.includes(window.location.origin + window.location.pathname))).length) continue; + this.iframe.contentWindow.postMessage({ message: 'thx.referral.claim.create', uuid: ref.uuid, }, this.settings.widgetUrl); + + const index = this.referrals.findIndex((r) => ref.uuid); + this.referrals.splice(index, 1); + } + + if (!this.referrals.length) { + window.clearInterval(this.timer); + } + } + + onMatchMedia(x) { + if (x.matches) { + const iframe = document.getElementById('thx-iframe'); + Object.assign(iframe.style, this.defaultStyles['sm']); + } else { + const iframe = document.getElementById('thx-iframe'); + Object.assign(iframe.style, this.defaultStyles['md']); + } + } + } + window.THXWidget = new THXWidget({ + apiUrl: '${API_URL}', + isPublished: window.location.origin.includes("${DASHBOARD_URL}") || ${widget.isPublished}, + widgetUrl: '${WIDGET_URL}', + poolId: '${req.params.id}', + chainId: '${pool.chainId}', + title: '${pool.settings.title}', + cssSelector: '${widget.cssSelector || ''}', + logoUrl: '${brand && brand.logoImgUrl ? brand.logoImgUrl : AUTH_URL + '/img/logo-padding.png'}', + backgroundUrl: '${brand && brand.backgroundImgUrl ? brand.backgroundImgUrl : ''}', + iconImg: '${widget.iconImg || ''}', + message: '${widget.message || ''}', + align: '${widget.align || 'right'}', + theme: '${widget.theme}', + refs: ${JSON.stringify(refs)}, + expired: '${expired}', + identity: '${req.query.identity || ''}', + containerSelector: '${req.query.containerSelector || ''}' + }); +} +`; + const result = await minify(data, { + mangle: { toplevel: false }, + sourceMap: NODE_ENV !== 'production', + }); + + res.set({ 'Content-Type': 'application/javascript' }).send(result.code); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/widget/widget.router.ts b/apps/api/src/app/controllers/widget/widget.router.ts new file mode 100644 index 000000000..bfaf56d29 --- /dev/null +++ b/apps/api/src/app/controllers/widget/widget.router.ts @@ -0,0 +1,29 @@ +import express, { Request, Response, NextFunction } from 'express'; +import { assertRequestInput } from '@thxnetwork/api/middlewares'; +import * as ReadWidget from './get.controller'; +import * as ReadWidgetScript from './js/get.controller'; +import { Pool } from '@thxnetwork/api/models'; + +const router: express.Router = express.Router(); + +router.get('/:id.:ext', assertRequestInput(ReadWidgetScript.validation), ReadWidgetScript.controller); +router.get( + '/:id', + async (req: Request, res: Response, next: NextFunction) => { + const isMongoId = (str: string) => { + const objectIdPattern = /^[0-9a-fA-F]{24}$/; + return objectIdPattern.test(str); + }; + + if (!isMongoId(req.params.id)) { + const pool = await Pool.findOne({ 'settings.slug': req.params.id }); + req.params.id = String(pool._id); + } + + next(); + }, + assertRequestInput(ReadWidget.validation), + ReadWidget.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/widgets/get.controller.ts b/apps/api/src/app/controllers/widgets/get.controller.ts new file mode 100644 index 000000000..4526c8f58 --- /dev/null +++ b/apps/api/src/app/controllers/widgets/get.controller.ts @@ -0,0 +1,12 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Widget } from '@thxnetwork/api/models'; + +const validation = [param('uuid').exists()]; + +const controller = async (req: Request, res: Response) => { + const widget = await Widget.findOne({ uuid: req.params.uuid }); + res.json(widget); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/widgets/list.controller.ts b/apps/api/src/app/controllers/widgets/list.controller.ts new file mode 100644 index 000000000..0ff3a66b7 --- /dev/null +++ b/apps/api/src/app/controllers/widgets/list.controller.ts @@ -0,0 +1,9 @@ +import { Request, Response } from 'express'; +import { Widget } from '@thxnetwork/api/models'; + +const controller = async (req: Request, res: Response) => { + const widgets = await Widget.find({ poolId: req.header('X-PoolId') }); + res.json(widgets); +}; + +export { controller }; diff --git a/apps/api/src/app/controllers/widgets/patch.controller.ts b/apps/api/src/app/controllers/widgets/patch.controller.ts new file mode 100644 index 000000000..b994d386d --- /dev/null +++ b/apps/api/src/app/controllers/widgets/patch.controller.ts @@ -0,0 +1,33 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { Widget } from '@thxnetwork/api/models'; + +const validation = [ + body('isPublished').optional().isBoolean(), + body('iconImg').optional().isString(), + body('align').optional().isString(), + body('theme').optional().isString(), + body('cssSelector').optional().isString(), + body('domain').optional().isURL({ require_tld: false }), + body('message').optional().isString().isLength({ max: 280 }).trim().escape(), +]; + +const controller = async (req: Request, res: Response) => { + const widget = await Widget.findOneAndUpdate( + { uuid: req.params.uuid }, + { + isPublished: req.body.isPublished, + iconImg: req.body.iconImg, + color: req.body.color, + align: req.body.align, + domain: req.body.domain, + message: req.body.message, + theme: req.body.theme, + cssSelector: req.body.cssSelector, + }, + { new: true }, + ); + return res.json(widget); +}; + +export { controller, validation }; diff --git a/apps/api/src/app/controllers/widgets/widgets.router.ts b/apps/api/src/app/controllers/widgets/widgets.router.ts new file mode 100644 index 000000000..be680b0b7 --- /dev/null +++ b/apps/api/src/app/controllers/widgets/widgets.router.ts @@ -0,0 +1,25 @@ +import express, { Router } from 'express'; +import { assertPoolAccess, assertRequestInput, guard } from '@thxnetwork/api/middlewares'; +import * as ReadWidget from './get.controller'; +import * as UpdateWidget from './patch.controller'; +import * as ListWidgets from './list.controller'; + +const router: express.Router = express.Router(); + +router.get('/', guard.check(['widgets:read']), assertPoolAccess, ListWidgets.controller); +router.get( + '/:uuid', + guard.check(['widgets:read']), + assertRequestInput(ReadWidget.validation), + assertPoolAccess, + ReadWidget.controller, +); +router.patch( + '/:uuid', + guard.check(['widgets:write', 'widgets:read']), + assertRequestInput(UpdateWidget.validation), + assertPoolAccess, + UpdateWidget.controller, +); + +export default router; diff --git a/apps/api/src/app/controllers/widgets/widgets.test.ts b/apps/api/src/app/controllers/widgets/widgets.test.ts new file mode 100644 index 000000000..7f86ace8b --- /dev/null +++ b/apps/api/src/app/controllers/widgets/widgets.test.ts @@ -0,0 +1,73 @@ +import request, { Response } from 'supertest'; +import app from '@thxnetwork/api/'; +import { ChainId } from '@thxnetwork/common/enums'; +import { dashboardAccessToken } from '@thxnetwork/api/util/jest/constants'; +import { afterAllCallback, beforeAllCallback } from '@thxnetwork/api/util/jest/config'; +import { WidgetDocument } from '@thxnetwork/api/models/Widget'; + +const user = request.agent(app); + +describe('Widgets', () => { + let poolId: string, widget: WidgetDocument; + const newTheme = + '{"elements":{"btnBg":{"label":"Button","color":"#FF0000"},"btnText":{"label":"Button Text","color":"#000000"},"text":{"label":"Text","color":"#ffffff"},"bodyBg":{"label":"Background","color":"#000000"},"cardBg":{"label":"Card","color":"#3b3b3b"},"navbarBg":{"label":"Navigation","color":"#3b3b3b"},"launcherBg":{"label":"Launcher","color":"#ffffff"},"launcherIcon":{"label":"Launcher Icon","color":"#000000"}},"colors":{"success":{"label":"Success","color":"#28a745"},"warning":{"label":"Warning","color":"#ffe500"},"danger":{"label":"Danger","color":"#dc3545"},"info":{"label":"Info","color":"#17a2b8"}}}', + align = 'left', + message = 'New message', + iconImg = 'https://image.icon'; + + beforeAll(beforeAllCallback); + afterAll(afterAllCallback); + + it('POST /pools', (done) => { + user.post('/v1/pools') + .set({ Authorization: dashboardAccessToken }) + .send({ chainId: ChainId.Hardhat }) + .expect(({ body }: Response) => { + poolId = body._id; + }) + .expect(201, done); + }); + + it('GET /widgets', (done) => { + user.get('/v1/widgets') + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .expect(({ body }: Response) => { + expect(body[0].uuid).toBeDefined(); + expect(body[0].theme).toBeDefined(); + expect(body[0].message).toEqual('Hi there!👋 Click me to complete quests and earn rewards...'); + widget = body[0]; + }) + .expect(200, done); + }); + + it('PATCH /widgets/:uuid', (done) => { + user.patch('/v1/widgets/' + widget.uuid) + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .send({ + iconImg, + align, + message, + theme: newTheme, + }) + .expect(({ body }: Response) => { + expect(body.iconImg).toBe(iconImg); + expect(body.uuid).toBeDefined(); + expect(body.theme).toEqual(newTheme); + expect(body.message).toEqual(message); + expect(body.align).toEqual(align); + }) + .expect(200, done); + }); + + it('GET /widgets/:uuid', (done) => { + user.get('/v1/widgets/' + widget.uuid) + .set({ 'X-PoolId': poolId, 'Authorization': dashboardAccessToken }) + .expect(({ body }: Response) => { + expect(body.iconImg).toBe(iconImg); + expect(body.uuid).toBeDefined(); + expect(body.theme).toEqual(newTheme); + expect(body.message).toEqual(message); + }) + .expect(200, done); + }); +}); diff --git a/apps/api/src/app/events/ClientReady.ts b/apps/api/src/app/events/ClientReady.ts new file mode 100644 index 000000000..64dc61a40 --- /dev/null +++ b/apps/api/src/app/events/ClientReady.ts @@ -0,0 +1,11 @@ +import { commands } from './commands/thx'; +import { Client } from 'discord.js'; +import { commandRegister } from '@thxnetwork/api/util/discord'; +import { logger } from '@thxnetwork/api/util/logger'; + +const onClientReady = async (client: Client<true>) => { + logger.info(`Ready! Logged in as ${client.user.tag}`); + await commandRegister(commands); +}; + +export default onClientReady; diff --git a/apps/api/src/app/events/GuildCreate.ts b/apps/api/src/app/events/GuildCreate.ts new file mode 100644 index 000000000..ea8a558c1 --- /dev/null +++ b/apps/api/src/app/events/GuildCreate.ts @@ -0,0 +1,17 @@ +import { Guild } from 'discord.js'; +import { logger } from '@thxnetwork/api/util/logger'; +import { handleError } from './commands/error'; + +const onGuildCreate = async (guild: Guild) => { + logger.info(`Added to guild: ${guild.name}`); + try { + const member = await guild.members.fetch(guild.ownerId); + await member.send({ + content: 'THX for the invite!🙏 Make sure to connect your campaign in THX Dashboard.', + }); + } catch (error) { + handleError(error); + } +}; + +export default onGuildCreate; diff --git a/apps/api/src/app/events/GuildDelete.ts b/apps/api/src/app/events/GuildDelete.ts new file mode 100644 index 000000000..b69ea94b8 --- /dev/null +++ b/apps/api/src/app/events/GuildDelete.ts @@ -0,0 +1,15 @@ +import { Guild } from 'discord.js'; +import { logger } from '@thxnetwork/api/util/logger'; +import { handleError } from './commands/error'; +import { DiscordGuild } from '@thxnetwork/api/models'; + +const onGuildDelete = async (guild: Guild) => { + try { + logger.info(`Removed campaign references for guild: ${guild.name}`); + await DiscordGuild.deleteMany({ guildId: guild.id }); + } catch (error) { + handleError(error); + } +}; + +export default onGuildDelete; diff --git a/apps/api/src/app/events/InteractionCreated.ts b/apps/api/src/app/events/InteractionCreated.ts new file mode 100644 index 000000000..3985d902b --- /dev/null +++ b/apps/api/src/app/events/InteractionCreated.ts @@ -0,0 +1,75 @@ +import { + ChatInputCommandInteraction, + StringSelectMenuInteraction, + ButtonInteraction, + AutocompleteInteraction, +} from 'discord.js'; +import { handleError } from './commands/error'; +import { onSelectQuestComplete, onClickQuestComplete, onClickRewardList, onClickQuestList } from './handlers/index'; +import { logger } from '../util/logger'; +import { DiscordGuild } from '@thxnetwork/api/models'; +import { Pool, PoolDocument } from '@thxnetwork/api/models'; +import router from './commands/thx'; + +export enum DiscordStringSelectMenuVariant { + QuestComplete = 'thx.campaign.quest.entry.create', + RewardBuy = 'thx.campaign.reward.payment.create', +} + +export enum DiscordButtonVariant { + RewardBuy = 'thx.campaign.reward.payment.create', + QuestComplete = 'thx.campaign.quest.entry.create', + QuestList = 'thx.campaign.quest.list', + RewardList = 'thx.campaign.reward.list', +} + +const stringSelectMenuMap = { + [DiscordStringSelectMenuVariant.QuestComplete]: onSelectQuestComplete, +}; + +export const onAutoComplete = async (interaction: AutocompleteInteraction) => { + if (!interaction.isAutocomplete()) return; + + const discordGuilds = await DiscordGuild.find({ guildId: interaction.guildId }); + const focusedValue = interaction.options.getFocused(); + const campaigns = await Promise.all(discordGuilds.map(({ poolId }) => Pool.findById(poolId))); + const choices = campaigns.filter((c) => !!c).map((c: PoolDocument) => `${c.settings.title}`); + const filtered = choices.filter((choice) => choice.startsWith(focusedValue)); + + await interaction.respond(filtered.map((choice) => ({ name: choice, value: choice }))); +}; + +const onInteractionCreated = async ( + interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, +) => { + try { + if (interaction.isButton()) { + logger.info(`#${interaction.user.id} clicked button #${interaction.customId}`); + if (interaction.customId.startsWith(DiscordButtonVariant.QuestComplete)) { + await onClickQuestComplete(interaction); + } + if (interaction.customId.startsWith(DiscordButtonVariant.QuestList)) { + await onClickQuestList(interaction); + } + if (interaction.customId.startsWith(DiscordButtonVariant.RewardList)) { + await onClickRewardList(interaction); + } + } + + if (interaction.isStringSelectMenu()) { + logger.info(`#${interaction.user.id} picked ${interaction.values[0]} for ${interaction.customId}`); + if (!stringSelectMenuMap[interaction.customId]) + throw new Error('Support for this action is not yet implemented!'); + await stringSelectMenuMap[interaction.customId](interaction); + } + + if (interaction.isCommand()) { + logger.info(`#${interaction.user.id} ran /${interaction.commandName}`); + router.executor(interaction); + } + } catch (error) { + handleError(error, interaction); + } +}; + +export default onInteractionCreated; diff --git a/apps/api/src/app/events/MessageCreate.ts b/apps/api/src/app/events/MessageCreate.ts new file mode 100644 index 000000000..ae2a63fb3 --- /dev/null +++ b/apps/api/src/app/events/MessageCreate.ts @@ -0,0 +1,59 @@ +import { Message } from 'discord.js'; +import { logger } from '../util/logger'; +import { DiscordMessage, DiscordGuild, QuestSocial, QuestSocialDocument } from '@thxnetwork/api/models'; +import { QuestSocialRequirement } from '@thxnetwork/common/enums'; +import AccountProxy from '../proxies/AccountProxy'; + +const onMessageCreate = async (message: Message) => { + try { + // Only record messages for connected accounts + const connectedAccount = await AccountProxy.getByDiscordId(message.author.id); + if (!connectedAccount) return; + + logger.info(`#${message.author.id} created message ${message.id} in guild ${message.guild.id}`); + + const start = new Date(); + start.setUTCHours(0, 0, 0, 0); + + const end = new Date(start); + end.setUTCHours(23, 59, 59, 999); + + const guild = await DiscordGuild.findOne({ guildId: message.guild.id }); + const quests = await QuestSocial.find({ + poolId: guild.poolId, + interaction: QuestSocialRequirement.DiscordMessage, + }); + if (!quests.length) return; + + // Return early if channel is not eligble for message tracking + const allowedChannels = quests.reduce((list: string[], q: QuestSocialDocument) => { + const { channels } = JSON.parse(q.contentMetadata); + return [...list, ...channels]; + }, []); + if (!allowedChannels.includes(message.channelId)) return; + + // Count the total amount of messages for today + const dailyMessageCount = await DiscordMessage.countDocuments({ + guildId: message.guild.id, + memberId: message.author.id, + createdAt: { $gte: start, $lt: end }, + }); + + // Get the highest limit for all available discord message quests in this campaign + const dailyMessageLimit = quests.reduce((highestLimit: number, quest: QuestSocialDocument) => { + const { limit } = JSON.parse(quest.contentMetadata); + return limit > highestLimit ? limit : highestLimit; + }, 0); + + // Only track messages if daily limit has not been surpassed + if (dailyMessageCount > dailyMessageLimit) return; + + // Store the message + const payload = { messageId: message.id, guildId: message.guild.id, memberId: message.author.id }; + await DiscordMessage.findOneAndUpdate(payload, payload, { upsert: true }); + } catch (error) { + logger.error(error); + } +}; + +export default onMessageCreate; diff --git a/apps/api/src/app/events/MessageReactionAdd.ts b/apps/api/src/app/events/MessageReactionAdd.ts new file mode 100644 index 000000000..3fc680414 --- /dev/null +++ b/apps/api/src/app/events/MessageReactionAdd.ts @@ -0,0 +1,33 @@ +import { MessageReaction } from 'discord.js'; +import { logger } from '../util/logger'; +import { DiscordReaction } from '@thxnetwork/api/models'; + +const onMessageReactionAdd = async (reaction: MessageReaction) => { + try { + const users = await reaction.users.fetch(); + const promises = users.map((user) => { + try { + // logger.info( + // `#${user.id} created a reaction on message ${reaction.message.id} in guild ${reaction.message.guild.id}`, + // ); + const filter = { + guildId: reaction.message.guild.id, + messageId: reaction.message.id, + memberId: user.id, + }; + return DiscordReaction.findOneAndUpdate( + filter, + { ...filter, content: reaction['_emoji'].name }, + { upsert: true }, + ); + } catch (error) { + logger.error(error); + } + }); + await Promise.all(promises); + } catch (error) { + logger.error(error); + } +}; + +export default onMessageReactionAdd; diff --git a/apps/api/src/app/events/commands/error.ts b/apps/api/src/app/events/commands/error.ts new file mode 100644 index 000000000..4f96d24a7 --- /dev/null +++ b/apps/api/src/app/events/commands/error.ts @@ -0,0 +1,20 @@ +import { ButtonInteraction, CommandInteraction, StringSelectMenuInteraction } from 'discord.js'; +import { logger } from '@thxnetwork/api/util/logger'; + +export const handleError = async ( + error: Error, + interaction?: ButtonInteraction | CommandInteraction | StringSelectMenuInteraction, +) => { + logger.info(error); + try { + if (interaction && interaction.isRepliable() && error.message) { + await interaction.reply({ + content: error.message, + ephemeral: true, + }); + } + } catch (error) { + logger.info(error); + // If the error reply fails we exit silently but log the cause + } +}; diff --git a/apps/api/src/app/events/commands/index.ts b/apps/api/src/app/events/commands/index.ts new file mode 100644 index 000000000..84139a19b --- /dev/null +++ b/apps/api/src/app/events/commands/index.ts @@ -0,0 +1,11 @@ +import quest from './thx/quest'; +import buy from './thx/buy'; +import points from './thx/points'; +import info from './thx/info'; + +export default { + quest, + buy, + points, + info, +}; diff --git a/apps/api/src/app/events/commands/thx.ts b/apps/api/src/app/events/commands/thx.ts new file mode 100644 index 000000000..78491ae1e --- /dev/null +++ b/apps/api/src/app/events/commands/thx.ts @@ -0,0 +1,64 @@ +import { CommandInteraction, SlashCommandBuilder } from 'discord.js'; +import { + DiscordCommandVariant, + onSubcommandBuy, + onSubcommandComplete, + onSubcommandInfo, + onSubcommandPoints, +} from './thx/index'; + +export const commands: any[] = [ + new SlashCommandBuilder() + .setName('info') + .setDescription('View your rank, quests and rewards in this campaign.') + .addStringOption((option) => + option.setName('campaign').setDescription('Campaign to search for').setAutocomplete(true), + ), + new SlashCommandBuilder().setName('quest').setDescription('Complete a quest and earn points.'), + new SlashCommandBuilder().setName('buy').setDescription('Buy a reward with points.'), + new SlashCommandBuilder() + .setName('remove-points') + .setDescription('Remove an amount of points for a user.') + .addUserOption((option) => + option.setName('user').setDescription('The user to transfer points to').setRequired(true), + ) + .addIntegerOption((option) => + option.setName('amount').setDescription('The amount of points to transfer').setRequired(true), + ) + .addStringOption((option) => + option.setName('campaign').setDescription('Campaign to search for').setAutocomplete(true), + ) + .addStringOption((option) => + option.setName('secret').setDescription('The optional secret for increased security'), + ), + new SlashCommandBuilder() + .setName('give-points') + .setDescription('Give an amount of points to a user.') + .addUserOption((option) => + option.setName('user').setDescription('The user to transfer points to').setRequired(true), + ) + .addIntegerOption((option) => + option.setName('amount').setDescription('The amount of points to transfer').setRequired(true), + ) + .addStringOption((option) => + option.setName('campaign').setDescription('Campaign to search for').setAutocomplete(true), + ) + .addStringOption((option) => + option.setName('secret').setDescription('The optional secret for increased security'), + ), +]; + +export default { + data: commands, + executor: (interaction: CommandInteraction) => { + const commandMap = { + 'quest': () => onSubcommandComplete(interaction), + 'buy': () => onSubcommandBuy(interaction), + 'info': () => onSubcommandInfo(interaction), + 'give-points': () => onSubcommandPoints(interaction, DiscordCommandVariant.GivePoints), + 'remove-points': () => onSubcommandPoints(interaction, DiscordCommandVariant.RemovePoints), + }; + const command = interaction.commandName; + if (commandMap[command]) commandMap[command](); + }, +}; diff --git a/apps/api/src/app/events/commands/thx/buy.ts b/apps/api/src/app/events/commands/thx/buy.ts new file mode 100644 index 000000000..23e87845c --- /dev/null +++ b/apps/api/src/app/events/commands/thx/buy.ts @@ -0,0 +1,18 @@ +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import { createSelectMenuRewards } from '@thxnetwork/api/events/components'; +import { CommandInteraction } from 'discord.js'; +import { handleError } from '../error'; + +export const onSubcommandBuy = async (interaction: CommandInteraction) => { + try { + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new Error('Please, connect your THX Account with Discord first.'); + + const row = await createSelectMenuRewards(interaction.guild); + + interaction.reply({ components: [row as any], ephemeral: true }); + } catch (error) { + handleError(error, interaction); + } +}; +export default { onSubcommandBuy }; diff --git a/apps/api/src/app/events/commands/thx/index.ts b/apps/api/src/app/events/commands/thx/index.ts new file mode 100644 index 000000000..2a08d413f --- /dev/null +++ b/apps/api/src/app/events/commands/thx/index.ts @@ -0,0 +1,4 @@ +export * from './quest'; +export * from './buy'; +export * from './points'; +export * from './info'; diff --git a/apps/api/src/app/events/commands/thx/info.ts b/apps/api/src/app/events/commands/thx/info.ts new file mode 100644 index 000000000..1cbcba9f5 --- /dev/null +++ b/apps/api/src/app/events/commands/thx/info.ts @@ -0,0 +1,85 @@ +import { ButtonStyle, CommandInteraction, Embed } from 'discord.js'; +import { Participant, Widget, Brand, Pool } from '@thxnetwork/api/models'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import DiscordDataProxy from '@thxnetwork/api/proxies/DiscordDataProxy'; +import { DiscordButtonVariant } from '../../InteractionCreated'; +import { handleError } from '../error'; +import { getDiscordGuild } from './points'; + +export const onSubcommandInfo = async (interaction: CommandInteraction) => { + try { + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new Error('Please, connect your Discord.'); + + const { discordGuild, error } = await getDiscordGuild(interaction); + if (error) throw new Error(error); + + const pool = await Pool.findById(discordGuild.poolId); + if (!pool) throw new Error('Could not find connected campaign.'); + + const participant = await Participant.findOne({ poolId: pool._id, sub: account.sub }); + if (!participant) throw new Error('You have not participated in the campaign yet.'); + + const brand = await Brand.findOne({ poolId: pool._id }); + const widget = await Widget.findOne({ poolId: pool._id }); + const theme = JSON.parse(widget.theme); + const color = parseInt(theme.elements.btnBg.color.replace(/^#/, ''), 16); + + const row = DiscordDataProxy.createButtonActionRow([ + { + style: ButtonStyle.Primary, + label: 'Quests', + customId: DiscordButtonVariant.QuestList, + emoji: `✅`, + }, + { + style: ButtonStyle.Primary, + label: 'Rewards', + customId: DiscordButtonVariant.RewardList, + emoji: `🎁`, + }, + { + style: ButtonStyle.Link, + label: 'Campaign URL', + url: `${pool.campaignURL}`, + }, + ]); + + const embed: any = { + title: `${pool.settings.title}`, + description: pool.settings.description ? `${pool.settings.description}` : ` `, + color, + fields: [ + { + name: `Name`, + value: `${account.username}`, + }, + { + name: `Points`, + value: participant ? `${participant.balance}` : '0', + inline: true, + }, + { + name: `Rank`, + value: participant && participant.rank > 0 ? `#${participant.rank}` : 'None', + inline: true, + }, + ], + } as Embed; + + if (brand && brand.backgroundImgUrl) { + embed['image'] = { url: brand && brand.backgroundImgUrl }; + } + + if (brand && brand.logoImgUrl) { + embed['thumbnail'] = { + url: brand.logoImgUrl, + }; + } + + interaction.reply({ embeds: [embed], components: [row as any], ephemeral: true }); + } catch (error) { + handleError(error, interaction); + } +}; +export default { onSubcommandInfo }; diff --git a/apps/api/src/app/events/commands/thx/points.ts b/apps/api/src/app/events/commands/thx/points.ts new file mode 100644 index 000000000..55173f2cc --- /dev/null +++ b/apps/api/src/app/events/commands/thx/points.ts @@ -0,0 +1,141 @@ +import { ButtonInteraction, CommandInteraction, User } from 'discord.js'; +import { WIDGET_URL } from '@thxnetwork/api/config/secrets'; +import { handleError } from '../error'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import PointBalanceService from '@thxnetwork/api/services/PointBalanceService'; +import { PoolDocument, Participant, DiscordGuild, Pool } from '@thxnetwork/api/models'; + +export enum DiscordCommandVariant { + GivePoints = 0, + RemovePoints = 1, +} + +async function removePoints( + pool: PoolDocument, + sender: TAccount, + receiver: TAccount, + senderUser: User, + receiverUser: User, + amount: number, +) { + await PointBalanceService.subtract(pool, receiver, amount); + + const participant = await Participant.findOne({ + poolId: pool._id, + sub: receiver.sub, + }); + + const senderMessage = `The balance of <@${receiverUser.id}> has been decreased with **${amount} points** and is now **${participant.balance}**.`; + const receiverMessage = `<@${senderUser.id}> decreased your balance with **${amount}** resulting in a total of **${participant.balance} points**.`; + + return { senderMessage, receiverMessage }; +} + +async function addPoints( + pool: PoolDocument, + sender: TAccount, + receiver: TAccount, + senderUser: User, + receiverUser: User, + amount: number, +) { + await PointBalanceService.add(pool, receiver, amount); + + const participant = await Participant.findOne({ + poolId: pool._id, + sub: receiver.sub, + }); + const senderMessage = `The balance of <@${receiverUser.id}> has been increased with **${amount} points** and is now **${participant.balance}**!`; + const receiverMessage = `<@${senderUser.id}> increased your balance with **${amount}** resulting in a total of **${participant.balance} points**.`; + + return { senderMessage, receiverMessage }; +} + +const pointsFunctionMap = { + [DiscordCommandVariant.GivePoints]: addPoints, + [DiscordCommandVariant.RemovePoints]: removePoints, +}; + +export async function getDiscordGuild(interaction: CommandInteraction | ButtonInteraction) { + const discordGuilds = await DiscordGuild.find({ guildId: interaction.guild.id }); + if (!discordGuilds.length) return { error: 'No campaign found ' }; + if (discordGuilds.length === 1) return { discordGuild: discordGuilds[0] }; + + const choice = ((interaction as CommandInteraction).options as any).getString('campaign'); + if (!choice) return { error: 'Please, select a campaign for this command.' }; + + const campaign = await Pool.findOne({ 'settings.title': choice }); + if (!campaign) return { error: 'Could not find campaing for this choice.' }; + + const discordGuild = discordGuilds.find((g) => g.poolId === String(campaign._id)); + return { discordGuild }; +} + +export const onSubcommandPoints = async (interaction: CommandInteraction, variant: DiscordCommandVariant) => { + try { + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new Error('Please, connect your THX Account with Discord first.'); + + const query = interaction.options.get('user').value as string; + if (!query) throw new Error('Please, provide a valid username.'); + + const result = await interaction.guild.members.search({ query, limit: 1 }); + if (!result) throw new Error('Could not find user'); + if (!result[query]) throw new Error('Could not find user'); + const user = result[query]; + if (!result[query]) throw new Error('Could not find user in search result'); + + const { discordGuild, error } = await getDiscordGuild(interaction); + if (error) throw new Error(error); + + // Check optional secret + const secret = interaction.options.get('secret'); + if (discordGuild.secret && discordGuild.secret.length) { + if (!secret) throw new Error('Please, provide a secret.'); + if (discordGuild.secret !== secret.value) throw new Error('Please, provide a valid secret.'); + } + + // Check role + const member = await interaction.guild.members.fetch(interaction.user.id); + if (!member.roles.cache.has(discordGuild.adminRoleId)) { + const role = await interaction.guild.roles.fetch(discordGuild.adminRoleId); + throw new Error(`Only **${role.name}** roles have access to this command!`); + } + + const amount = interaction.options.get('amount'); + if (!amount.value || Number(amount.value) < 1) throw new Error('Please, provide a valid amount.'); + + const pool = await Pool.findById(discordGuild.poolId); + if (!pool) throw new Error('Could not find connected campaign.'); + + const receiver = await AccountProxy.getByDiscordId(user.id); + if (!receiver) { + user.send({ + content: `<@${interaction.user.id}> failed to send you ${amount.value} points. Please [sign in](${WIDGET_URL}/c/${pool._id}), connect Discord and notify the sender!`, + }); + throw new Error('Please, ask the receiver to connect a Discord account.'); + } + + // Determine if we should add or remove using pointsFunctionMap + const { senderMessage, receiverMessage } = await pointsFunctionMap[variant]( + pool, + account, + receiver, + interaction.user, + user, + Number(amount.value), + ); + + // Send reaction to caller + interaction.reply({ + content: senderMessage, + ephemeral: true, + }); + + // Send DM to user + user.send({ content: receiverMessage }); + } catch (error) { + handleError(error, interaction); + } +}; +export default { onSubcommandPoints }; diff --git a/apps/api/src/app/events/commands/thx/quest.ts b/apps/api/src/app/events/commands/thx/quest.ts new file mode 100644 index 000000000..5c969579f --- /dev/null +++ b/apps/api/src/app/events/commands/thx/quest.ts @@ -0,0 +1,21 @@ +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import { createSelectMenuQuests } from '@thxnetwork/api/events/components'; +import { CommandInteraction } from 'discord.js'; +import { handleError } from '../error'; + +export const onSubcommandComplete = async (interaction: CommandInteraction) => { + try { + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new Error('Please, connect your THX Account with Discord first.'); + + const row = await createSelectMenuQuests(interaction); + if (!row) { + interaction.reply({ content: 'No quests found for this campaign.', ephemeral: true }); + } else { + interaction.reply({ components: [row as any], ephemeral: true }); + } + } catch (error) { + handleError(error, interaction); + } +}; +export default { onSubcommandComplete }; diff --git a/apps/api/src/app/events/components/index.ts b/apps/api/src/app/events/components/index.ts new file mode 100644 index 000000000..363a3f634 --- /dev/null +++ b/apps/api/src/app/events/components/index.ts @@ -0,0 +1,2 @@ +export * from './selectMenuQuests'; +export * from './selectMenuRewards'; diff --git a/apps/api/src/app/events/components/selectMenuQuests.ts b/apps/api/src/app/events/components/selectMenuQuests.ts new file mode 100644 index 000000000..35c534d10 --- /dev/null +++ b/apps/api/src/app/events/components/selectMenuQuests.ts @@ -0,0 +1,75 @@ +import { + ActionRowBuilder, + ButtonInteraction, + CommandInteraction, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, +} from 'discord.js'; +import { DiscordStringSelectMenuVariant } from '../InteractionCreated'; +import { QuestInvite } from '@thxnetwork/api/models/QuestInvite'; +import { QuestWeb3 } from '@thxnetwork/api/models/QuestWeb3'; +import { questInteractionVariantMap } from '@thxnetwork/common/maps'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import { + DiscordGuild, + Pool, + PoolDocument, + QuestCustom, + QuestDaily, + QuestGitcoin, + QuestSocial, +} from '@thxnetwork/api/models'; + +async function findQuests(campaigns: PoolDocument[]) { + const poolId = campaigns.map(({ _id }) => String(_id)); + return await Promise.all([ + QuestDaily.find({ poolId, isPublished: true }), + QuestInvite.find({ poolId, isPublished: true }), + QuestSocial.find({ poolId, isPublished: true }), + QuestCustom.find({ poolId, isPublished: true }), + QuestWeb3.find({ poolId, isPublished: true }), + QuestGitcoin.find({ poolId, isPublished: true }), + ]); +} + +async function createSelectMenuQuests(interaction: CommandInteraction | ButtonInteraction) { + const discordGuilds = await DiscordGuild.find({ guildId: interaction.guild.id }); + if (!discordGuilds.length) throw new Error('Could not find server.'); + + const poolId = discordGuilds.map((g) => g.poolId); + const campaigns = await Pool.find({ _id: poolId }); + if (!campaigns.length) throw new Error('No campaigns found for this server.'); + + const select = new StringSelectMenuBuilder(); + select.setCustomId(DiscordStringSelectMenuVariant.QuestComplete).setPlaceholder('Complete a quest'); + + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new Error('No THX account found for this Discord user.'); + + const quests = (await findQuests(campaigns)).flat(); + if (!quests.length) throw new Error('No quests found for this campaign.'); + + for (const index in quests) { + const quest: any = quests[index]; + + // Campaign might be removed + const campaign = campaigns.find((c) => String(c._id) === quest.poolId); + if (!campaign) continue; + + const questId = String(quest._id); + const variant = quest.interaction ? questInteractionVariantMap[quest.interaction] : quest.variant; + const value = JSON.stringify({ questId, variant }); + const amount = await QuestService.getAmount(variant, quest, account); + const options = new StringSelectMenuOptionBuilder() + .setLabel(`[${amount}] ${quest.title}`) + .setDescription(`${campaign.settings.title}`) + .setValue(value); + + select.addOptions(options); + } + + return new ActionRowBuilder().addComponents(select); +} + +export { createSelectMenuQuests }; diff --git a/apps/api/src/app/events/components/selectMenuRewards.ts b/apps/api/src/app/events/components/selectMenuRewards.ts new file mode 100644 index 000000000..397f3ff7c --- /dev/null +++ b/apps/api/src/app/events/components/selectMenuRewards.ts @@ -0,0 +1,43 @@ +import { RewardVariant } from '@thxnetwork/common/enums'; +import { ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, Guild } from 'discord.js'; +import { DiscordStringSelectMenuVariant } from '../InteractionCreated'; +import { + DiscordGuild, + RewardNFT, + RewardDiscordRole, + RewardCoin, + RewardCustom, + RewardCoupon, +} from '@thxnetwork/api/models'; + +async function createSelectMenuRewards(guild: Guild) { + const { poolId } = await DiscordGuild.findOne({ guildId: guild.id }); + const results = await Promise.all([ + RewardCoin.find({ poolId, pointPrice: { $gt: 0 } }), + RewardNFT.find({ poolId, pointPrice: { $gt: 0 } }), + RewardCustom.find({ poolId, pointPrice: { $gt: 0 } }), + RewardCoupon.find({ poolId, pointPrice: { $gt: 0 } }), + RewardDiscordRole.find({ poolId, pointPrice: { $gt: 0 } }), + ]); + const rewards = results.flat(); + if (!rewards.length) throw new Error('No rewards found for this campaign.'); + + const select = new StringSelectMenuBuilder(); + select.setCustomId(DiscordStringSelectMenuVariant.RewardBuy).setPlaceholder('Buy a reward'); + + for (const index in rewards) { + const reward = rewards[index]; + const questId = String(reward._id); + const value = JSON.stringify({ questId, variant: reward.variant }); + const options = new StringSelectMenuOptionBuilder() + .setLabel(reward.title) + .setDescription(`${reward.pointPrice} points (${RewardVariant[reward.variant]} Reward)`) + .setValue(value); + + select.addOptions(options); + } + + return new ActionRowBuilder().addComponents(select); +} + +export { createSelectMenuRewards }; diff --git a/apps/api/src/app/events/handlers/button/quest.ts b/apps/api/src/app/events/handlers/button/quest.ts new file mode 100644 index 000000000..315c3ea10 --- /dev/null +++ b/apps/api/src/app/events/handlers/button/quest.ts @@ -0,0 +1,28 @@ +import { ButtonInteraction } from 'discord.js'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import { completeQuest } from '@thxnetwork/api/events/handlers/select/quest'; +import { handleError } from '../../commands/error'; +import { createSelectMenuQuests } from '../../components'; + +export async function onClickQuestComplete(interaction: ButtonInteraction) { + try { + // Custom Id Syntax: DiscordButtonVariant.QuestComplete + ':' + questVariant ':' + questId + const data = interaction.customId.split(':'); + const variant = data[1] as unknown as QuestVariant; + const questId = data[2]; + + await completeQuest(interaction, variant, questId); + } catch (error) { + handleError(error, interaction); + } +} + +export async function onClickQuestList(interaction: ButtonInteraction) { + try { + const row = await createSelectMenuQuests(interaction); + + interaction.reply({ components: [row as any], ephemeral: true }); + } catch (error) { + handleError(error, interaction); + } +} diff --git a/apps/api/src/app/events/handlers/button/reward.ts b/apps/api/src/app/events/handlers/button/reward.ts new file mode 100644 index 000000000..c88234877 --- /dev/null +++ b/apps/api/src/app/events/handlers/button/reward.ts @@ -0,0 +1,60 @@ +import { ButtonInteraction } from 'discord.js'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import { handleError } from '../../commands/error'; +import { + RewardCoin, + RewardNFT, + DiscordGuild, + RewardCustom, + RewardCoupon, + RewardDiscordRole, +} from '@thxnetwork/api/models'; + +export async function onClickRewardRedeem(interaction: ButtonInteraction) { + try { + // Custom Id Syntax: DiscordButtonVariant.QuestComplete + ':' + questVariant ':' + questId + const data = interaction.customId.split(':'); + const variant = data[1] as unknown as RewardVariant; + const questId = data[2]; + + // await completeReward(interaction, variant, questId); + } catch (error) { + handleError(error, interaction); + } +} + +export async function onClickRewardList(interaction: ButtonInteraction) { + try { + const discordGuild = await DiscordGuild.findOne({ guildId: interaction.guild.id }); + if (!discordGuild) throw new Error('Could not find this Discord server.'); + + const { poolId } = discordGuild; + const results = await Promise.all([ + RewardCoin.find({ poolId, pointPrice: { $gt: 0 } }), + RewardNFT.find({ poolId, pointPrice: { $gt: 0 } }), + RewardCustom.find({ poolId, pointPrice: { $gt: 0 } }), + RewardCoupon.find({ poolId, pointPrice: { $gt: 0 } }), + RewardDiscordRole.find({ poolId, pointPrice: { $gt: 0 } }), + ]); + const rewards = results.flat(); + if (!rewards.length) throw new Error('No rewards found for this campaign.'); + + const list = rewards.map( + (reward: any) => + `${String(reward.pointPrice).padStart(4)} pts. ${reward.title} (${RewardVariant[reward.variant]})`, + ); + const code = list.join('\n'); + const embeds = [ + { + title: `🎁 Rewards`, + description: rewards.length + ? 'Use `/buy` to buy rewards with points. \n ```' + code + `\n` + '```' + : 'No rewards available!', + }, + ]; + + interaction.reply({ embeds, ephemeral: true }); + } catch (error) { + handleError(error, interaction); + } +} diff --git a/apps/api/src/app/events/handlers/index.ts b/apps/api/src/app/events/handlers/index.ts new file mode 100644 index 000000000..280d715e4 --- /dev/null +++ b/apps/api/src/app/events/handlers/index.ts @@ -0,0 +1,3 @@ +export * from './select/quest'; +export * from './button/quest'; +export * from './button/reward'; diff --git a/apps/api/src/app/events/handlers/select/quest.ts b/apps/api/src/app/events/handlers/select/quest.ts new file mode 100644 index 000000000..3ef04e548 --- /dev/null +++ b/apps/api/src/app/events/handlers/select/quest.ts @@ -0,0 +1,138 @@ +import { ButtonInteraction, ButtonStyle, StringSelectMenuInteraction } from 'discord.js'; +import { JobType, QuestVariant } from '@thxnetwork/common/enums'; +import { handleError } from '../../commands/error'; +import { DiscordButtonVariant } from '../../InteractionCreated'; +import { Widget, Brand } from '@thxnetwork/api/models'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import { DiscordDisconnected } from '@thxnetwork/api/util/errors'; +import { serviceMap } from '@thxnetwork/api/services/interfaces/IQuestService'; +import AccountProxy from '@thxnetwork/api/proxies/AccountProxy'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import QuestService from '@thxnetwork/api/services/QuestService'; +import DiscordDataProxy from '@thxnetwork/api/proxies/DiscordDataProxy'; + +export async function completeQuest( + interaction: ButtonInteraction | StringSelectMenuInteraction, + variant: QuestVariant, + questId: string, +) { + try { + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new DiscordDisconnected(); + + const Quest = serviceMap[variant].models.quest; + const quest = await Quest.findById(questId); + if (!quest) throw new Error('Could not find this quest.'); + + const pool = await PoolService.getById(quest.poolId); + if (!pool) throw new Error('Could not find this campaign.'); + + const { interaction: questInteraction } = quest as TQuestSocial; + const platformUserId = questInteraction && QuestService.findUserIdForInteraction(account, questInteraction); + + const data = { + isClaimed: true, + platformUserId, + }; + const availabilityValidation = await QuestService.isAvailable(variant, { + quest, + account, + data, + }); + if (!availabilityValidation.result) throw new Error(availabilityValidation.reason); + + const requirementValidation = await QuestService.getValidationResult(variant, { + quest, + account, + data, + }); + if (!requirementValidation.result) throw new Error(requirementValidation.reason); + + const amount = await QuestService.getAmount(variant, quest, account); + + await agenda.now(JobType.CreateQuestEntry, { + variant, + questId: quest._id, + sub: account.sub, + data, + }); + + interaction.reply({ + content: `Completed **${quest.title}** and earned **${amount} points**.`, + ephemeral: true, + }); + } catch (error) { + handleError(error, interaction); + } +} + +export async function onSelectQuestComplete(interaction: StringSelectMenuInteraction) { + try { + const { questId, variant } = JSON.parse(interaction.values[0]); + + const account = await AccountProxy.getByDiscordId(interaction.user.id); + if (!account) throw new DiscordDisconnected(); + + const quest = await QuestService.findById(variant, questId); + if (!quest) throw new Error('Could not find this quest.'); + + const pool = await PoolService.getById(quest.poolId); + if (!pool) throw new Error('Could not find this campaign.'); + + const data = {}; + const isAvailable = await QuestService.isAvailable(variant, { quest, account, data }); + const brand = await Brand.findOne({ poolId: pool._id }); + const widget = await Widget.findOne({ poolId: pool._id }); + const theme = JSON.parse(widget.theme); + const amount = await QuestService.getAmount(quest.variant, quest, account); + const embedQuest = { + title: quest.title, + description: quest.description, + author: { + name: pool.settings.title, + icon_url: brand ? brand.logoImgUrl : '', + url: widget.domain, + }, + image: { url: quest.image }, + color: parseInt(theme.elements.btnBg.color.replace(/^#/, ''), 16), + fields: [ + { + name: 'Points', + value: `${amount}`, + inline: true, + }, + { + name: 'Type', + value: `${QuestVariant[quest.variant]}`, + inline: true, + }, + ], + }; + + const row = DiscordDataProxy.createButtonActionRow([ + { + emoji: isAvailable ? '✅' : '🔒', + label: 'Complete', + style: ButtonStyle.Success, + customId: `${DiscordButtonVariant.QuestComplete}:${variant}:${questId}`, + disabled: !isAvailable, + }, + { + label: 'More Info', + style: ButtonStyle.Link, + url: `${pool.campaignURL}/quests`, + }, + ]); + const components = []; + components.push(row); + + interaction.reply({ + ephemeral: true, + content: '', + embeds: [embedQuest], + components, + }); + } catch (error) { + handleError(error, interaction); + } +} diff --git a/apps/api/src/app/events/index.ts b/apps/api/src/app/events/index.ts new file mode 100644 index 000000000..14fd91d46 --- /dev/null +++ b/apps/api/src/app/events/index.ts @@ -0,0 +1,16 @@ +import { Events } from 'discord.js'; +import onClientReady from './ClientReady'; +import onInteractionCreated from './InteractionCreated'; +import onMessageReactionAdd from './MessageReactionAdd'; +import onMessageCreate from './MessageCreate'; +import onGuildCreate from './GuildCreate'; +import onGuildDelete from './GuildDelete'; + +export default { + [Events.ClientReady]: onClientReady, + [Events.GuildCreate]: onGuildCreate, + [Events.GuildDelete]: onGuildDelete, + [Events.InteractionCreate]: onInteractionCreated, + [Events.MessageReactionAdd]: onMessageReactionAdd, + [Events.MessageCreate]: onMessageCreate, +}; diff --git a/apps/api/src/app/hardhat/.gitignore b/apps/api/src/app/hardhat/.gitignore new file mode 100644 index 000000000..e8c12ff4f --- /dev/null +++ b/apps/api/src/app/hardhat/.gitignore @@ -0,0 +1,17 @@ +node_modules +.env + +# Hardhat files +/cache +/artifacts + +# TypeChain files +/typechain +/typechain-types + +# solidity-coverage files +/coverage +/coverage.json + +# Hardhat Ignition default folder for deployments against a local node +ignition/deployments/chain-31337 diff --git a/apps/api/src/app/hardhat/contracts/ImportSafe.sol b/apps/api/src/app/hardhat/contracts/ImportSafe.sol new file mode 100644 index 000000000..5cdd89f4c --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/ImportSafe.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.4; + +import '@gnosis.pm/safe-contracts/contracts/accessors/SimulateTxAccessor.sol'; +import '@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol'; +import '@gnosis.pm/safe-contracts/contracts/handler/DefaultCallbackHandler.sol'; +// import '@gnosis.pm/safe-contracts/contracts/handler/CompatibilityFallbackHandler.sol'; +import '@gnosis.pm/safe-contracts/contracts/libraries/CreateCall.sol'; +import '@gnosis.pm/safe-contracts/contracts/libraries/MultiSend.sol'; +import '@gnosis.pm/safe-contracts/contracts/libraries/MultiSendCallOnly.sol'; +import '@gnosis.pm/safe-contracts/contracts/examples/libraries/SignMessage.sol'; +import '@gnosis.pm/safe-contracts/contracts/GnosisSafeL2.sol'; +import '@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol'; + +// Get the compiler to pick up these facets +contract ImportSafe { + SimulateTxAccessor public simulateTxAccessor; + GnosisSafeProxyFactory public gnosisSafeProxyFactory; + DefaultCallbackHandler public defaultCallbackHandler; + // CompatibilityFallbackHandler public compatibilityFallbackHandler; + CreateCall public createCall; + MultiSend public multiSend; + MultiSendCallOnly public multiSendCallOnly; + SignMessageLib public signMessageLib; + GnosisSafeL2 public gnosisSafeL2; + GnosisSafe public gnosisSafe; +} diff --git a/apps/api/src/app/hardhat/contracts/interfaces/IAccessControlEvents.sol b/apps/api/src/app/hardhat/contracts/interfaces/IAccessControlEvents.sol new file mode 100644 index 000000000..548d77c8b --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/interfaces/IAccessControlEvents.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.6; + +interface IAccessControlEvents { + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); +} diff --git a/apps/api/src/app/hardhat/contracts/mock/BAL.sol b/apps/api/src/app/hardhat/contracts/mock/BAL.sol new file mode 100644 index 000000000..63c7a3a69 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/BAL.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract BAL is ERC20 { + constructor(address to, uint256 amount) ERC20('Balancer', 'BAL') { + _mint(to, amount); + } + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } +} diff --git a/apps/api/src/app/hardhat/contracts/mock/BPT.sol b/apps/api/src/app/hardhat/contracts/mock/BPT.sol new file mode 100644 index 000000000..886f3f1a2 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/BPT.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +interface IWeightedPool is IERC20 { + function setVault(address _vault) external; + function getVault() external view returns (address); + function getPoolId() external view returns (bytes32); +} + +contract BPT is ERC20, IWeightedPool { + address public vault; + + constructor(address _to, uint256 _amount) ERC20('20USDC-80THX', '20USDC-80THX') { + _mint(_to, _amount); + } + + function setVault(address _vault) external override { + vault = _vault; + } + + function getVault() external override view returns (address) { + return vault; + } + + function getPoolId() external override pure returns (bytes32) { + return 0xb204bf10bc3a5435017d3db247f56da601dfe08a0002000000000000000000fe; + } +} diff --git a/apps/api/src/app/hardhat/contracts/mock/BPTGauge.sol b/apps/api/src/app/hardhat/contracts/mock/BPTGauge.sol new file mode 100644 index 000000000..99e1a65dd --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/BPTGauge.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma abicoder v2; +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +interface IGauge is IERC20 { + function deposit(uint256 _value) external; + function withdraw(uint256 _value) external; + function lp_token() external view returns (address); + function working_supply() external view returns (uint256); +} + +contract BPTGauge is ERC20, IGauge { + IERC20 public bpt; + + event Deposit(address indexed user, uint256 value); + event Withdraw(address indexed user, uint256 value); + + constructor(address _bpt) ERC20('Balancer 20USDC-80THX Gauge Deposit', '20USDC-80THX-gauge') { + bpt = IERC20(_bpt); + } + + /* + * @dev Deposit LP tokens in the gauge and mint BPTGauge for msg.sender + * @param _value Amount of LP tokens to deposit + */ + function deposit(uint256 _value) external override { + // Transfer BPT from the user to the gauge + bpt.transferFrom(msg.sender, address(this), _value); + + // Mint BPTGauge tokens to the user + _mint(msg.sender, _value); + + emit Deposit(msg.sender, _value); + } + + /* + * @dev Withdraw LP tokens from the gauge + * @param _value Amount of LP tokens to withdraw + * @notice This mock function will not decrease the totalSupply + */ + function withdraw(uint256 _value) external override { + // Transfer staked BPT from the gauge to the user + bpt.transfer(msg.sender, _value); + + // Burn BPTGauge for the user + transferFrom(msg.sender, address(0), _value); + + emit Withdraw(msg.sender, _value); + } + + function lp_token() external override view returns (address) { + return address(bpt); + } + + function working_supply() external override pure returns (uint256) { + return 585909572986408132343905; + } +} diff --git a/apps/api/src/app/hardhat/contracts/mock/BalancerVault.sol b/apps/api/src/app/hardhat/contracts/mock/BalancerVault.sol new file mode 100644 index 000000000..005373a23 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/BalancerVault.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma abicoder v2; +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import "@openzeppelin/contracts/math/SafeMath.sol"; +import 'hardhat/console.sol'; + +contract BalancerVault { + using SafeMath for uint256; + + ERC20 public bpt; + ERC20 public usdc; + ERC20 public thx; + + struct JoinPoolRequest { + address[] assets; + uint256[] maxAmountsIn; + bytes userData; + bool fromInternalBalance; + } + + constructor(address _bpt, address _usdc, address _thx) { + bpt = ERC20(_bpt); + usdc = ERC20(_usdc); + thx = ERC20(_thx); + } + + function joinPool(bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request) external { + usdc.transferFrom(sender, address(this), request.maxAmountsIn[0]); + thx.transferFrom(sender, address(this), request.maxAmountsIn[1]); + + // Assumes BalancerVault has a BPT balance and transfers BPT to recipient + // Aligns decimals in order to get to a workable BPT amount + uint256 usdcAmount = request.maxAmountsIn[0].div(10**usdc.decimals()); + uint256 thxAmount = request.maxAmountsIn[1].div(10**thx.decimals()); + uint256 amount = usdcAmount.add(thxAmount).mul(10**bpt.decimals()); + + require(bpt.transfer(recipient, amount), 'BalancerVault: BPT transfer failed'); + } +} diff --git a/apps/api/src/app/hardhat/contracts/mock/ExampleToken.sol b/apps/api/src/app/hardhat/contracts/mock/ExampleToken.sol new file mode 100644 index 000000000..1188cbdaa --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/ExampleToken.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract ExampleToken is ERC20 { + constructor(address to, uint256 amount) ERC20('Test Token', 'TEST') { + _mint(to, amount); + } +} diff --git a/apps/api/src/app/hardhat/contracts/mock/THX.sol b/apps/api/src/app/hardhat/contracts/mock/THX.sol new file mode 100644 index 000000000..a9005b389 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/THX.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract THX is ERC20 { + constructor(address to, uint256 amount) ERC20('THX Network (PoS)', 'THX') { + _mint(to, amount); + } +} diff --git a/apps/api/src/app/hardhat/contracts/mock/USDC.sol b/apps/api/src/app/hardhat/contracts/mock/USDC.sol new file mode 100644 index 000000000..fb8e47d00 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/mock/USDC.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract USDC is ERC20 { + constructor(address to, uint256 amount) ERC20('USD Coin (PoS)', 'USDC.e') { + _setupDecimals(6); + _mint(to, amount); + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/BondPurchaseChecker.sol b/apps/api/src/app/hardhat/contracts/utils/BondPurchaseChecker.sol new file mode 100644 index 000000000..1070088cd --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/BondPurchaseChecker.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma abicoder v2; +pragma solidity ^0.7.6; + +interface ICustomBill { + struct Bill { + uint256 payout; + uint256 payoutClaimed; + uint256 vesting; + uint256 vestingTerm; + uint256 vestingStartTimestamp; + uint256 lastClaimTimestamp; + uint256 truePricePaid; + } + + function getBillIds(address user) external view returns (uint[] memory); + function getBillInfo(uint256 billId) external view returns (Bill memory); +} + +contract BondPurchaseChecker { + ICustomBill bond; + + constructor(address _bondContractAddress) { + bond = ICustomBill(_bondContractAddress); + } + + function largestPayoutOf(address _user) public view returns (uint256) { + uint[] memory billIds = bond.getBillIds(_user); + uint256 largestPayout = 0; + + for (uint i = 0; i < billIds.length; i++) { + uint256 payout = bond.getBillInfo(billIds[i]).payout; + largestPayout = payout > largestPayout ? payout : largestPayout; + } + + return largestPayout; + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC1155/ITHX_ERC1155.sol b/apps/api/src/app/hardhat/contracts/utils/ERC1155/ITHX_ERC1155.sol new file mode 100644 index 000000000..6d5bb9c4c --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC1155/ITHX_ERC1155.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol'; + +interface ITHX_ERC1155 is IERC1155 { + function mint(address to, uint256 id, uint256 amount, bytes memory data) external; + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) external; +} \ No newline at end of file diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC1155/THX_ERC1155.sol b/apps/api/src/app/hardhat/contracts/utils/ERC1155/THX_ERC1155.sol new file mode 100644 index 000000000..dc1935fc5 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC1155/THX_ERC1155.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.7.6; +import '@openzeppelin/contracts/token/ERC1155/ERC1155.sol'; +import '@openzeppelin/contracts/access/AccessControl.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts/utils/Strings.sol'; + +contract THX_ERC1155 is ERC1155, AccessControl, Ownable { + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); + + constructor(string memory URI_, address owner_) ERC1155(URI_) { + transferOwnership(owner_); + _setupRole(DEFAULT_ADMIN_ROLE, owner_); + _setupRole(MINTER_ROLE, owner_); + } + + function mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public { + require(hasRole(MINTER_ROLE, msg.sender), 'NOT_MINTER'); + _mint(to, id, amount, data); + } + + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public { + require(hasRole(MINTER_ROLE, msg.sender), 'NOT_MINTER'); + _mintBatch(to, ids, amounts, data); + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC20/LimitedSupplyToken.sol b/apps/api/src/app/hardhat/contracts/utils/ERC20/LimitedSupplyToken.sol new file mode 100644 index 000000000..d65a01def --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC20/LimitedSupplyToken.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +/******************************************************************************\ +* @title ERC20 Limited Supply +* @author Peter Polman <peter@thx.network> +* @notice Used for point systems with a limited supply. Mints the full supply to the to argument given in the contructor. +* @dev Not upgradable contract. +/******************************************************************************/ + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract LimitedSupplyToken is ERC20 { + constructor( + string memory _name, + string memory _symbol, + address to, + uint256 amount + ) ERC20(_name, _symbol) { + _mint(to, amount); + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC20/THX_ERC20.sol b/apps/api/src/app/hardhat/contracts/utils/ERC20/THX_ERC20.sol new file mode 100644 index 000000000..6ed0955b8 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC20/THX_ERC20.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +/******************************************************************************\ +* @title ERC20 Limited Supply +* @author Peter Polman <peter@thx.network> +* @notice Used for point systems with a limited supply. Mints the full supply to the to argument given in the contructor. +* @dev Not upgradable contract. +/******************************************************************************/ + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract THX_ERC20_LimitedSupply is ERC20 { + constructor(string memory _name, string memory _symbol, address to, uint256 amount) ERC20(_name, _symbol) { + _mint(to, amount); + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC20/THX_ERC20_UnlimitedSupply.sol b/apps/api/src/app/hardhat/contracts/utils/ERC20/THX_ERC20_UnlimitedSupply.sol new file mode 100644 index 000000000..7c6acc951 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC20/THX_ERC20_UnlimitedSupply.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +/******************************************************************************\ +* @title ERC20 Unlimited Supply +* @author Evert Kors <evert@thx.network> +* @notice Used for point systems with an unlimited supply. Mints the required tokens whenever they are needed. +* @dev Not upgradable contract. +/******************************************************************************/ + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts/access/AccessControl.sol'; + +contract THX_ERC20_UnlimitedSupply is ERC20, AccessControl, Ownable { + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); + + constructor(string memory name_, string memory symbol_, address owner_) ERC20(name_, symbol_) { + transferOwnership(owner_); + _setupRole(DEFAULT_ADMIN_ROLE, owner_); + _setupRole(MINTER_ROLE, owner_); + } + + function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal override { + if (hasRole(MINTER_ROLE, _from)) { + _mint(_from, _amount); + } + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC20/UnlimitedSupplyToken.sol b/apps/api/src/app/hardhat/contracts/utils/ERC20/UnlimitedSupplyToken.sol new file mode 100644 index 000000000..2ebafbbdf --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC20/UnlimitedSupplyToken.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.7.6; + +/******************************************************************************\ +* @title ERC20 Unlimited Supply +* @author Evert Kors <evert@thx.network> +* @notice Used for point systems with an unlimited supply. Mints the required tokens whenever they are needed. +* @dev Not upgradable contract. +/******************************************************************************/ + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts/access/AccessControl.sol'; + +contract UnlimitedSupplyToken is ERC20, AccessControl, Ownable { + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); + + constructor( + string memory name_, + string memory symbol_, + address owner_ + ) ERC20(name_, symbol_) { + transferOwnership(owner_); + _setupRole(DEFAULT_ADMIN_ROLE, owner_); + _setupRole(MINTER_ROLE, owner_); + } + + function _beforeTokenTransfer( + address _from, + address _to, + uint256 _amount + ) internal override { + if (hasRole(MINTER_ROLE, _from)) { + _mint(_from, _amount); + } + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC721/INonFungibleToken.sol b/apps/api/src/app/hardhat/contracts/utils/ERC721/INonFungibleToken.sol new file mode 100644 index 000000000..37e4741cd --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC721/INonFungibleToken.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; + +interface INonFungibleToken is IERC721 { + function mint(address _recipient, string memory _tokenURI) external returns (uint256); +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC721/ITHX_ERC721.sol b/apps/api/src/app/hardhat/contracts/utils/ERC721/ITHX_ERC721.sol new file mode 100644 index 000000000..f9795535f --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC721/ITHX_ERC721.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; + +interface ITHX_ERC721 is IERC721 { + function mint(address _recipient, string memory _tokenURI) external returns (uint256); +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC721/NonFungibleToken.sol b/apps/api/src/app/hardhat/contracts/utils/ERC721/NonFungibleToken.sol new file mode 100644 index 000000000..b6779d648 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC721/NonFungibleToken.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; +import '@openzeppelin/contracts/utils/Counters.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts/access/AccessControl.sol'; +import './INonFungibleToken.sol'; + +contract NonFungibleToken is INonFungibleToken, ERC721, AccessControl, Ownable { + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); + using Counters for Counters.Counter; + Counters.Counter private _tokenIds; + + constructor( + string memory name_, + string memory symbol_, + string memory baseURI_, + address owner_ + ) ERC721(name_, symbol_) { + transferOwnership(owner_); + _setupRole(DEFAULT_ADMIN_ROLE, owner_); + _setupRole(MINTER_ROLE, owner_); + _setBaseURI(baseURI_); + } + + function mint(address _recipient, string memory _tokenURI) external override returns (uint256) { + require(hasRole(MINTER_ROLE, msg.sender), 'NOT_MINTER'); + + _tokenIds.increment(); + + uint256 newItemId = _tokenIds.current(); + _mint(_recipient, newItemId); + _setTokenURI(newItemId, _tokenURI); + + return newItemId; + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/ERC721/THX_ERC721.sol b/apps/api/src/app/hardhat/contracts/utils/ERC721/THX_ERC721.sol new file mode 100644 index 000000000..7a2eef72a --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/ERC721/THX_ERC721.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; +import '@openzeppelin/contracts/utils/Counters.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts/access/AccessControl.sol'; +import './ITHX_ERC721.sol'; + +contract THX_ERC721 is ITHX_ERC721, ERC721, AccessControl, Ownable { + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); + using Counters for Counters.Counter; + Counters.Counter private _tokenIds; + + constructor( + string memory name_, + string memory symbol_, + string memory baseURI_, + address owner_ + ) ERC721(name_, symbol_) { + transferOwnership(owner_); + _setupRole(DEFAULT_ADMIN_ROLE, owner_); + _setupRole(MINTER_ROLE, owner_); + _setBaseURI(baseURI_); + } + + function mint(address _recipient, string memory _tokenURI) external override returns (uint256) { + require(hasRole(MINTER_ROLE, msg.sender), 'NOT_MINTER'); + + _tokenIds.increment(); + + uint256 newItemId = _tokenIds.current(); + _mint(_recipient, newItemId); + _setTokenURI(newItemId, _tokenURI); + + return newItemId; + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/THXMembershipPurchaser.sol b/apps/api/src/app/hardhat/contracts/utils/THXMembershipPurchaser.sol new file mode 100644 index 000000000..854ee84c1 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/THXMembershipPurchaser.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma abicoder v2; +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; +import '@openzeppelin/contracts/utils/Context.sol'; +import '@openzeppelin/contracts/math/SafeMath.sol'; +import '../mock/BalancerVault.sol'; +import '../mock/BPTGauge.sol'; +import '../mock/BPT.sol'; +import './THXRegistry.sol'; + +contract THXMembershipPurchaser is ReentrancyGuard, Context { + address admin; + ITHXRegistry public registry; + IWeightedPool public bpt; + IGauge public gauge; + BalancerVault public vault; + + constructor(address _admin, address _registry) { + admin = _admin; + registry = ITHXRegistry(_registry); + gauge = BPTGauge(registry.getGauge()); + bpt = IWeightedPool(gauge.lp_token()); + vault = BalancerVault(bpt.getVault()); + } + + /* + * Joins the Balancer pool with the provided _tokenIn and stakes the received BPT-gauge tokens. On success the BPT-gauge tokens are + * transferred back to the caller and can be locked. + * @param _owner Owner of the payment balance + * @param _amountIn Amount of _tokenIn to provide as liquidity and stake + * @param _minAmountOut Minimum amount of BPT to receive + */ + function joinAndStake(address _owner, address _tokenIn, uint256 _amountIn, uint256 _minAmountOut) external nonReentrant { + // @notice Avoids having the caller approve for multiple transfers + require( + IERC20(_tokenIn).transferFrom(_owner, address(this), _amountIn), + 'MembershipPurchaser: transfer failed' + ); + + // Define the assets used for the Balancer pool join + address[] memory _assets = new address[](2); + _assets[0] = _tokenIn; + _assets[1] = address(0); + + // Define the max amounts in for the Balancer pool join + uint256[] memory _maxAmountsIn = new uint256[](2); + _maxAmountsIn[0] = _amountIn; + _maxAmountsIn[1] = 0; + + // Create Balancer liqiuidity position for _amountIn in _tokenIn + IERC20(_tokenIn).approve(address(vault), _maxAmountsIn[0]); + vault.joinPool(bpt.getPoolId(), address(this), address(this), BalancerVault.JoinPoolRequest({ + assets: _assets, + maxAmountsIn: _maxAmountsIn, + userData: abi.encode(1, _maxAmountsIn, _minAmountOut), // WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT = 1 + fromInternalBalance: false + })); + + // Approve and stake all received BPT and receive BPT-gauge tokens + uint256 bptAmount = bpt.balanceOf(address(this)); + bpt.approve(address(gauge), bptAmount); + gauge.deposit(bptAmount); + + // Transfer BPT-gauge to the RewardDistributor for further distribution + uint256 bptGaugeAmount = gauge.balanceOf(address(this)); + gauge.transfer(_owner, bptGaugeAmount); + } + +} diff --git a/apps/api/src/app/hardhat/contracts/utils/THXPaymentSplitter.sol b/apps/api/src/app/hardhat/contracts/utils/THXPaymentSplitter.sol new file mode 100644 index 000000000..924bd6849 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/THXPaymentSplitter.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma abicoder v2; +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; +import '@openzeppelin/contracts/utils/Context.sol'; +import '@openzeppelin/contracts/math/SafeMath.sol'; +import '../mock/BalancerVault.sol'; +import '../mock/BPTGauge.sol'; +import '../mock/BPT.sol'; +import './THXRegistry.sol'; + +contract THXPaymentSplitter is ReentrancyGuard, Context { + using SafeMath for uint256; + + address admin; + ITHXRegistry public registry; + IGauge public gauge; + IWeightedPool public bpt; + BalancerVault public vault; + IERC20 public paymentToken; + + mapping(address => uint256) public rates; + mapping(address => uint256) private _balances; + mapping(address => uint256) private _lastPaymentAt; + + constructor(address _admin, address _registry) { + admin = _admin; + registry = ITHXRegistry(_registry); + paymentToken = IERC20(registry.getPaymentToken()); + gauge = BPTGauge(registry.getGauge()); + bpt = IWeightedPool(gauge.lp_token()); + vault = BalancerVault(bpt.getVault()); + } + + // @dev Only admin can change this + function setRegistry(address _registry) external { + require(_msgSender() == admin, 'PaymentSplitter: !admin'); + registry = ITHXRegistry(_registry); + } + + // Set the USDC rate in wei per second for the sender + function setRate(address _owner, uint256 _rate) external { + require(_msgSender() == admin, 'PaymentSplitter: !admin'); + rates[_owner] = _rate; + } + + /* + * Transfers USDC to the payee based on the payout rate. Then it adds liquidity for the remainder of the deposit amount and stakes the + * received BPT. On success the balance and last payment timestamp are stored in reference to the owner and used when fetching returning + * the balance. + * @param _owner Owner of the payment balance + * @param _amount Amount of USDC to deposit + * + * @notice Assumes paymentToken allowance for address(this) is sufficient for the call + * @dev ReentrancyGuard is implemented for the deposit call in case THXRegistry ever gets compromised + */ + function deposit(address _owner, uint256 _amount, uint256 _minAmountOut) external nonReentrant { + // Don't allow payments if no rate is set for owner + require(rates[_owner] > 0, 'PaymentSplitter: !rate'); + + // Transfer deposit amount to self for further splitting. + // @notice Avoids having the caller approve for multiple transfers + require( + paymentToken.transferFrom(_owner, address(this), _amount), + 'PaymentSplitter: transfer failed' + ); + + // Transfer payout to payee in paymentToken as per payout rate determined by registry + uint256 payoutRate = registry.getPayoutRate(); + uint256 payout = _amount.mul(payoutRate).div(10000); // 100% * 100 to allow for 2 decimals without devisisons + + require( + paymentToken.transfer(registry.getPayee(), payout), + 'PaymentSplitter: transfer failed' + ); + + // Define the assets used for the Balancer pool join + address[] memory _assets = new address[](2); + _assets[0] = address(paymentToken); + _assets[1] = address(0); + + // Subtract the payout that's transferred to the payee before setting the amounts in for pool join + uint256[] memory _maxAmountsIn = new uint256[](2); + _maxAmountsIn[0] = _amount.sub(payout); + _maxAmountsIn[1] = 0; + + // Create Balancer liqiuidity position for remainder of the _amount + paymentToken.approve(address(vault), _maxAmountsIn[0]); + vault.joinPool(bpt.getPoolId(), address(this), address(this), BalancerVault.JoinPoolRequest({ + assets: _assets, + maxAmountsIn: _maxAmountsIn, + userData: abi.encode(1, _maxAmountsIn, _minAmountOut), // WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT = 1 + fromInternalBalance: false + })); + + // Approve and stake all received BPT and receive BPT-gauge tokens + // TODO Potentially harmful if multiple transactions are mined in the same block + uint256 bptAmount = bpt.balanceOf(address(this)); + bpt.approve(address(gauge), bptAmount); + gauge.deposit(bptAmount); + + // Transfer BPT-gauge to the RewardDistributor for further distribution + uint256 bptGaugeAmount = gauge.balanceOf(address(this)); + require(gauge.transfer(registry.getRewardDistributor(), bptGaugeAmount), 'PaymentSplitter: transfer failed'); + + // Increase total balance for _owner + _balances[_owner] = _balances[_owner].add(_amount); + + // Store lastPaymentAt timestamp for _owner + _lastPaymentAt[_owner] = block.timestamp; + } + + /* + * @return Current payment balance of owner measured since last payment made and rate set + */ + function balanceOf(address _owner) public view returns (uint256) { + uint256 rate = rates[_owner]; // USDC rate in wei per second for _owner + uint256 lastPaymentAt = _lastPaymentAt[_owner]; // block.timestamp of last payment + // If no payments have been made return 0 + if (lastPaymentAt == 0) { + return 0; + } + + // Required balances equals seconds since last payment * rate per second + uint256 currentBlock = block.timestamp; + + // Will always be positive since lastPaymentAt is always a historic timestamp + uint256 balanceRequired = currentBlock.sub(lastPaymentAt).mul(rate); + + // If balance is insufficient return 0 + if (_balances[_owner] < balanceRequired) { + return 0; + } + + // Safe to subtract as we have checked for balance to be greater than balanceRequired + return _balances[_owner].sub(balanceRequired); + } +} diff --git a/apps/api/src/app/hardhat/contracts/utils/THXRegistry.sol b/apps/api/src/app/hardhat/contracts/utils/THXRegistry.sol new file mode 100644 index 000000000..097375e75 --- /dev/null +++ b/apps/api/src/app/hardhat/contracts/utils/THXRegistry.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.7.6; + +interface ITHXRegistry { + function getRewardDistributor() external view returns (address); + function getPayoutRate() external view returns (uint256); + function getPayee() external view returns (address); + function getPaymentToken() external view returns (address); + function getGauge() external view returns (address); +} + +contract THXRegistry is ITHXRegistry { + address public paymentToken; // USDC + address public payee; + uint256 public payoutRate; + address public rewardDistributor; // VoteEscrow RewardDistributor + address public gauge; // Balancer ChildChain Liquidity Gauge + + /* + * @param _paymentToken Token address used for payouts to payee + * @param _payee Payee address receiving payouts as per payoutRate + * @param _rewardDistributor VoteEscrow RewardDistributor address + * @param _gauge Balancer child chain gauge address + * @param _payoutRate Share of payouts to payee as percentage times 100 (3000) + */ + constructor(address _paymentToken, address _payee, address _rewardDistributor, address _gauge) { + paymentToken = _paymentToken; + payee = _payee; + rewardDistributor = _rewardDistributor; + gauge = _gauge; + } + + function getRewardDistributor() external view override returns (address) { + return rewardDistributor; + } + + function getPayoutRate() external view override returns (uint256) { + return payoutRate; + } + + // @notice Payout rate as a percentage times 100 (3000 for 30%) + // @dev Should be governed with parameter voting + function setPayoutRate(uint256 _rate) external { + require(msg.sender == payee, 'THXRegistry: !payee'); + require(_rate >= 0 && _rate <= 10000, 'THXRegistry: payoutRate out of bounds'); + payoutRate = _rate; + } + + function getPayee() external view override returns (address) { + return payee; + } + + function getPaymentToken() external view override returns (address) { + return paymentToken; + } + + function getGauge() external view override returns (address) { + return gauge; + } + +} diff --git a/apps/api/src/app/hardhat/export/BAL.json b/apps/api/src/app/hardhat/export/BAL.json new file mode 100644 index 000000000..d1e75ca5a --- /dev/null +++ b/apps/api/src/app/hardhat/export/BAL.json @@ -0,0 +1,315 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "BAL", + "sourceName": "contracts/mock/BAL.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000e2538038062000e25833981810160405260408110156200003757600080fd5b50805160209182015160408051808201825260088152672130b630b731b2b960c11b818601908152825180840190935260038084526210905360ea1b968401969096528151949593949193620000909290919062000240565b508051620000a690600490602084019062000240565b50506005805460ff1916601217905550620000c28282620000ca565b5050620002ec565b6001600160a01b03821662000126576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6200013460008383620001d9565b6200015081600254620001de60201b620005ba1790919060201c565b6002556001600160a01b0382166000908152602081815260409091205462000183918390620005ba620001de821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b60008282018381101562000239576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002785760008555620002c3565b82601f106200029357805160ff1916838001178555620002c3565b82800160010185558215620002c3579182015b82811115620002c3578251825591602001919060010190620002a6565b50620002d1929150620002d5565b5090565b5b80821115620002d15760008155600101620002d6565b610b2980620002fc6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c806340c10f191161007157806340c10f191461021057806370a082311461023e57806395d89b4114610264578063a457c2d71461026c578063a9059cbb14610298578063dd62ed3e146102c4576100b4565b806306fdde03146100b9578063095ea7b31461013657806318160ddd1461017657806323b872dd14610190578063313ce567146101c657806339509351146101e4575b600080fd5b6100c16102f2565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fb5781810151838201526020016100e3565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101626004803603604081101561014c57600080fd5b506001600160a01b038135169060200135610388565b604080519115158252519081900360200190f35b61017e6103a5565b60408051918252519081900360200190f35b610162600480360360608110156101a657600080fd5b506001600160a01b038135811691602081013590911690604001356103ab565b6101ce610432565b6040805160ff9092168252519081900360200190f35b610162600480360360408110156101fa57600080fd5b506001600160a01b03813516906020013561043b565b61023c6004803603604081101561022657600080fd5b506001600160a01b038135169060200135610489565b005b61017e6004803603602081101561025457600080fd5b50356001600160a01b0316610497565b6100c16104b2565b6101626004803603604081101561028257600080fd5b506001600160a01b038135169060200135610513565b610162600480360360408110156102ae57600080fd5b506001600160a01b03813516906020013561057b565b61017e600480360360408110156102da57600080fd5b506001600160a01b038135811691602001351661058f565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b820191906000526020600020905b81548152906001019060200180831161036157829003601f168201915b5050505050905090565b600061039c61039561061b565b848461061f565b50600192915050565b60025490565b60006103b884848461070b565b610428846103c461061b565b61042385604051806060016040528060288152602001610a5e602891396001600160a01b038a1660009081526001602052604081209061040261061b565b6001600160a01b031681526020810191909152604001600020549190610866565b61061f565b5060019392505050565b60055460ff1690565b600061039c61044861061b565b84610423856001600061045961061b565b6001600160a01b03908116825260208083019390935260409182016000908120918c1681529252902054906105ba565b61049382826108fd565b5050565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b600061039c61052061061b565b8461042385604051806060016040528060258152602001610acf602591396001600061054a61061b565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190610866565b600061039c61058861061b565b848461070b565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600082820183811015610614576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b0383166106645760405162461bcd60e51b8152600401808060200182810382526024815260200180610aab6024913960400191505060405180910390fd5b6001600160a01b0382166106a95760405162461bcd60e51b8152600401808060200182810382526022815260200180610a166022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107505760405162461bcd60e51b8152600401808060200182810382526025815260200180610a866025913960400191505060405180910390fd5b6001600160a01b0382166107955760405162461bcd60e51b81526004018080602001828103825260238152602001806109f36023913960400191505060405180910390fd5b6107a08383836109ed565b6107dd81604051806060016040528060268152602001610a38602691396001600160a01b0386166000908152602081905260409020549190610866565b6001600160a01b03808516600090815260208190526040808220939093559084168152205461080c90826105ba565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108f55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156108ba5781810151838201526020016108a2565b50505050905090810190601f1680156108e75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216610958576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b610964600083836109ed565b60025461097190826105ba565b6002556001600160a01b03821660009081526020819052604090205461099790826105ba565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122054d3aaaafb4397e9ba9a956156cfdf7c50a74c32a8e663bdf85e3a80c501d06464736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100b45760003560e01c806340c10f191161007157806340c10f191461021057806370a082311461023e57806395d89b4114610264578063a457c2d71461026c578063a9059cbb14610298578063dd62ed3e146102c4576100b4565b806306fdde03146100b9578063095ea7b31461013657806318160ddd1461017657806323b872dd14610190578063313ce567146101c657806339509351146101e4575b600080fd5b6100c16102f2565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fb5781810151838201526020016100e3565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101626004803603604081101561014c57600080fd5b506001600160a01b038135169060200135610388565b604080519115158252519081900360200190f35b61017e6103a5565b60408051918252519081900360200190f35b610162600480360360608110156101a657600080fd5b506001600160a01b038135811691602081013590911690604001356103ab565b6101ce610432565b6040805160ff9092168252519081900360200190f35b610162600480360360408110156101fa57600080fd5b506001600160a01b03813516906020013561043b565b61023c6004803603604081101561022657600080fd5b506001600160a01b038135169060200135610489565b005b61017e6004803603602081101561025457600080fd5b50356001600160a01b0316610497565b6100c16104b2565b6101626004803603604081101561028257600080fd5b506001600160a01b038135169060200135610513565b610162600480360360408110156102ae57600080fd5b506001600160a01b03813516906020013561057b565b61017e600480360360408110156102da57600080fd5b506001600160a01b038135811691602001351661058f565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b820191906000526020600020905b81548152906001019060200180831161036157829003601f168201915b5050505050905090565b600061039c61039561061b565b848461061f565b50600192915050565b60025490565b60006103b884848461070b565b610428846103c461061b565b61042385604051806060016040528060288152602001610a5e602891396001600160a01b038a1660009081526001602052604081209061040261061b565b6001600160a01b031681526020810191909152604001600020549190610866565b61061f565b5060019392505050565b60055460ff1690565b600061039c61044861061b565b84610423856001600061045961061b565b6001600160a01b03908116825260208083019390935260409182016000908120918c1681529252902054906105ba565b61049382826108fd565b5050565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b600061039c61052061061b565b8461042385604051806060016040528060258152602001610acf602591396001600061054a61061b565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190610866565b600061039c61058861061b565b848461070b565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600082820183811015610614576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b0383166106645760405162461bcd60e51b8152600401808060200182810382526024815260200180610aab6024913960400191505060405180910390fd5b6001600160a01b0382166106a95760405162461bcd60e51b8152600401808060200182810382526022815260200180610a166022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107505760405162461bcd60e51b8152600401808060200182810382526025815260200180610a866025913960400191505060405180910390fd5b6001600160a01b0382166107955760405162461bcd60e51b81526004018080602001828103825260238152602001806109f36023913960400191505060405180910390fd5b6107a08383836109ed565b6107dd81604051806060016040528060268152602001610a38602691396001600160a01b0386166000908152602081905260409020549190610866565b6001600160a01b03808516600090815260208190526040808220939093559084168152205461080c90826105ba565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108f55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156108ba5781810151838201526020016108a2565b50505050905090810190601f1680156108e75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216610958576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b610964600083836109ed565b60025461097190826105ba565b6002556001600160a01b03821660009081526020819052604090205461099790826105ba565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122054d3aaaafb4397e9ba9a956156cfdf7c50a74c32a8e663bdf85e3a80c501d06464736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/BPT.json b/apps/api/src/app/hardhat/export/BPT.json new file mode 100644 index 000000000..e39ae6960 --- /dev/null +++ b/apps/api/src/app/hardhat/export/BPT.json @@ -0,0 +1,349 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "BPT", + "sourceName": "contracts/mock/BPT.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getPoolId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + } + ], + "name": "setVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000e0738038062000e07833981810160405260408110156200003757600080fd5b508051602091820151604080518082018252600c8082526b06460aaa688865a7060a890b60a31b82870181815284518086019095529184529583019590955280519394929390926200008d91600391906200023d565b508051620000a39060049060208401906200023d565b50506005805460ff1916601217905550620000bf8282620000c7565b5050620002e9565b6001600160a01b03821662000123576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6200013160008383620001d6565b6200014d81600254620001db60201b6200068f1790919060201c565b6002556001600160a01b03821660009081526020818152604090912054620001809183906200068f620001db821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b60008282018381101562000236576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002755760008555620002c0565b82601f106200029057805160ff1916838001178555620002c0565b82800160010185558215620002c0579182015b82811115620002c0578251825591602001919060010190620002a3565b50620002ce929150620002d2565b5090565b5b80821115620002ce5760008155600101620002d3565b610b0e80620002f96000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80636817031b11610097578063a457c2d711610066578063a457c2d7146102d3578063a9059cbb146102ff578063dd62ed3e1461032b578063fbfa77cf14610359576100f5565b80636817031b1461025957806370a08231146102815780638d928af8146102a757806395d89b41146102cb576100f5565b806323b872dd116100d357806323b872dd146101d1578063313ce5671461020757806338fff2d014610225578063395093511461022d576100f5565b806306fdde03146100fa578063095ea7b31461017757806318160ddd146101b7575b600080fd5b610102610361565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561013c578181015183820152602001610124565b50505050905090810190601f1680156101695780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101a36004803603604081101561018d57600080fd5b506001600160a01b0381351690602001356103f7565b604080519115158252519081900360200190f35b6101bf610414565b60408051918252519081900360200190f35b6101a3600480360360608110156101e757600080fd5b506001600160a01b0381358116916020810135909116906040013561041a565b61020f6104a1565b6040805160ff9092168252519081900360200190f35b6101bf6104aa565b6101a36004803603604081101561024357600080fd5b506001600160a01b0381351690602001356104ce565b61027f6004803603602081101561026f57600080fd5b50356001600160a01b031661051c565b005b6101bf6004803603602081101561029757600080fd5b50356001600160a01b0316610544565b6102af61055f565b604080516001600160a01b039092168252519081900360200190f35b610102610573565b6101a3600480360360408110156102e957600080fd5b506001600160a01b0381351690602001356105d4565b6101a36004803603604081101561031557600080fd5b506001600160a01b03813516906020013561063c565b6101bf6004803603604081101561034157600080fd5b506001600160a01b0381358116916020013516610650565b6102af61067b565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103ed5780601f106103c2576101008083540402835291602001916103ed565b820191906000526020600020905b8154815290600101906020018083116103d057829003601f168201915b5050505050905090565b600061040b6104046106f0565b84846106f4565b50600192915050565b60025490565b60006104278484846107e0565b610497846104336106f0565b61049285604051806060016040528060288152602001610a43602891396001600160a01b038a166000908152600160205260408120906104716106f0565b6001600160a01b03168152602081019190915260400160002054919061093b565b6106f4565b5060019392505050565b60055460ff1690565b7fb204bf10bc3a5435017d3db247f56da601dfe08a0002000000000000000000fe90565b600061040b6104db6106f0565b8461049285600160006104ec6106f0565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549061068f565b600580546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6001600160a01b031660009081526020819052604090205490565b60055461010090046001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103ed5780601f106103c2576101008083540402835291602001916103ed565b600061040b6105e16106f0565b8461049285604051806060016040528060258152602001610ab4602591396001600061060b6106f0565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061093b565b600061040b6106496106f0565b84846107e0565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60055461010090046001600160a01b031681565b6000828201838110156106e9576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b0383166107395760405162461bcd60e51b8152600401808060200182810382526024815260200180610a906024913960400191505060405180910390fd5b6001600160a01b03821661077e5760405162461bcd60e51b81526004018080602001828103825260228152602001806109fb6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166108255760405162461bcd60e51b8152600401808060200182810382526025815260200180610a6b6025913960400191505060405180910390fd5b6001600160a01b03821661086a5760405162461bcd60e51b81526004018080602001828103825260238152602001806109d86023913960400191505060405180910390fd5b6108758383836109d2565b6108b281604051806060016040528060268152602001610a1d602691396001600160a01b038616600090815260208190526040902054919061093b565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546108e1908261068f565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156109ca5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561098f578181015183820152602001610977565b50505050905090810190601f1680156109bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122092787e3cbd971df817541c818008a4dc0435dc43da4a6c2f5e3e16fc0203cfd564736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80636817031b11610097578063a457c2d711610066578063a457c2d7146102d3578063a9059cbb146102ff578063dd62ed3e1461032b578063fbfa77cf14610359576100f5565b80636817031b1461025957806370a08231146102815780638d928af8146102a757806395d89b41146102cb576100f5565b806323b872dd116100d357806323b872dd146101d1578063313ce5671461020757806338fff2d014610225578063395093511461022d576100f5565b806306fdde03146100fa578063095ea7b31461017757806318160ddd146101b7575b600080fd5b610102610361565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561013c578181015183820152602001610124565b50505050905090810190601f1680156101695780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101a36004803603604081101561018d57600080fd5b506001600160a01b0381351690602001356103f7565b604080519115158252519081900360200190f35b6101bf610414565b60408051918252519081900360200190f35b6101a3600480360360608110156101e757600080fd5b506001600160a01b0381358116916020810135909116906040013561041a565b61020f6104a1565b6040805160ff9092168252519081900360200190f35b6101bf6104aa565b6101a36004803603604081101561024357600080fd5b506001600160a01b0381351690602001356104ce565b61027f6004803603602081101561026f57600080fd5b50356001600160a01b031661051c565b005b6101bf6004803603602081101561029757600080fd5b50356001600160a01b0316610544565b6102af61055f565b604080516001600160a01b039092168252519081900360200190f35b610102610573565b6101a3600480360360408110156102e957600080fd5b506001600160a01b0381351690602001356105d4565b6101a36004803603604081101561031557600080fd5b506001600160a01b03813516906020013561063c565b6101bf6004803603604081101561034157600080fd5b506001600160a01b0381358116916020013516610650565b6102af61067b565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103ed5780601f106103c2576101008083540402835291602001916103ed565b820191906000526020600020905b8154815290600101906020018083116103d057829003601f168201915b5050505050905090565b600061040b6104046106f0565b84846106f4565b50600192915050565b60025490565b60006104278484846107e0565b610497846104336106f0565b61049285604051806060016040528060288152602001610a43602891396001600160a01b038a166000908152600160205260408120906104716106f0565b6001600160a01b03168152602081019190915260400160002054919061093b565b6106f4565b5060019392505050565b60055460ff1690565b7fb204bf10bc3a5435017d3db247f56da601dfe08a0002000000000000000000fe90565b600061040b6104db6106f0565b8461049285600160006104ec6106f0565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549061068f565b600580546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6001600160a01b031660009081526020819052604090205490565b60055461010090046001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103ed5780601f106103c2576101008083540402835291602001916103ed565b600061040b6105e16106f0565b8461049285604051806060016040528060258152602001610ab4602591396001600061060b6106f0565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061093b565b600061040b6106496106f0565b84846107e0565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60055461010090046001600160a01b031681565b6000828201838110156106e9576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b0383166107395760405162461bcd60e51b8152600401808060200182810382526024815260200180610a906024913960400191505060405180910390fd5b6001600160a01b03821661077e5760405162461bcd60e51b81526004018080602001828103825260228152602001806109fb6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166108255760405162461bcd60e51b8152600401808060200182810382526025815260200180610a6b6025913960400191505060405180910390fd5b6001600160a01b03821661086a5760405162461bcd60e51b81526004018080602001828103825260238152602001806109d86023913960400191505060405180910390fd5b6108758383836109d2565b6108b281604051806060016040528060268152602001610a1d602691396001600160a01b038616600090815260208190526040902054919061093b565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546108e1908261068f565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156109ca5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561098f578181015183820152602001610977565b50505050905090810190601f1680156109bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122092787e3cbd971df817541c818008a4dc0435dc43da4a6c2f5e3e16fc0203cfd564736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/BPTGauge.json b/apps/api/src/app/hardhat/export/BPTGauge.json new file mode 100644 index 000000000..ddf9bd5ed --- /dev/null +++ b/apps/api/src/app/hardhat/export/BPTGauge.json @@ -0,0 +1,395 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "BPTGauge", + "sourceName": "contracts/mock/BPTGauge.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_bpt", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bpt", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "lp_token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "working_supply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000ff438038062000ff483398101604081905262000034916200018d565b60405180606001604052806023815260200162000fd160239139604051806040016040528060128152602001713230555344432d38305448582d676175676560701b815250816003908051906020019062000091929190620000e1565b508051620000a7906004906020840190620000e1565b5050600580546001600160a01b0390931661010002610100600160a81b031960ff19909416601217939093169290921790915550620001bd565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928262000119576000855562000164565b82601f106200013457805160ff191683800117855562000164565b8280016001018555821562000164579182015b828111156200016457825182559160200191906001019062000147565b506200017292915062000176565b5090565b5b8082111562000172576000815560010162000177565b6000602082840312156200019f578081fd5b81516001600160a01b0381168114620001b6578182fd5b9392505050565b610e0480620001cd6000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c8063546af3c311610097578063a457c2d711610066578063a457c2d7146101e8578063a9059cbb146101fb578063b6b55f251461020e578063dd62ed3e1461022157610100565b8063546af3c3146101b057806370a08231146101c557806382c63066146101d857806395d89b41146101e057610100565b806323b872dd116100d357806323b872dd146101605780632e1a7d4d14610173578063313ce56714610188578063395093511461019d57610100565b806306fdde0314610105578063095ea7b31461012357806317e280891461014357806318160ddd14610158575b600080fd5b61010d610234565b60405161011a9190610c63565b60405180910390f35b610136610131366004610ba6565b6102ca565b60405161011a9190610c58565b61014b6102e7565b60405161011a9190610cb6565b61014b6102f5565b61013661016e366004610b6b565b6102fb565b610186610181366004610bef565b610382565b005b61019061045c565b60405161011a9190610cbf565b6101366101ab366004610ba6565b610465565b6101b86104b3565b60405161011a9190610c07565b61014b6101d3366004610b1f565b6104c7565b6101b86104e6565b61010d6104fa565b6101366101f6366004610ba6565b61055b565b610136610209366004610ba6565b6105c3565b61018661021c366004610bef565b6105d7565b61014b61022f366004610b39565b6106a5565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102c05780601f10610295576101008083540402835291602001916102c0565b820191906000526020600020905b8154815290600101906020018083116102a357829003601f168201915b5050505050905090565b60006102de6102d76106d0565b84846106d4565b50600192915050565b697c1238ba7d2d4f8ef86190565b60025490565b60006103088484846107c0565b610378846103146106d0565b61037385604051806060016040528060288152602001610d39602891396001600160a01b038a166000908152600160205260408120906103526106d0565b6001600160a01b03168152602081019190915260400160002054919061091b565b6106d4565b5060019392505050565b60055460405163a9059cbb60e01b81526101009091046001600160a01b03169063a9059cbb906103b89033908590600401610c3f565b602060405180830381600087803b1580156103d257600080fd5b505af11580156103e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040a9190610bcf565b50610417336000836102fb565b50336001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364826040516104519190610cb6565b60405180910390a250565b60055460ff1690565b60006102de6104726106d0565b8461037385600160006104836106d0565b6001600160a01b03908116825260208083019390935260409182016000908120918c1681529252902054906109b2565b60055461010090046001600160a01b031681565b6001600160a01b0381166000908152602081905260409020545b919050565b60055461010090046001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102c05780601f10610295576101008083540402835291602001916102c0565b60006102de6105686106d0565b8461037385604051806060016040528060258152602001610daa60259139600160006105926106d0565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061091b565b60006102de6105d06106d0565b84846107c0565b6005546040516323b872dd60e01b81526101009091046001600160a01b0316906323b872dd9061060f90339030908690600401610c1b565b602060405180830381600087803b15801561062957600080fd5b505af115801561063d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106619190610bcf565b5061066c3382610a13565b336001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c826040516104519190610cb6565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166107195760405162461bcd60e51b8152600401808060200182810382526024815260200180610d866024913960400191505060405180910390fd5b6001600160a01b03821661075e5760405162461bcd60e51b8152600401808060200182810382526022815260200180610cf16022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166108055760405162461bcd60e51b8152600401808060200182810382526025815260200180610d616025913960400191505060405180910390fd5b6001600160a01b03821661084a5760405162461bcd60e51b8152600401808060200182810382526023815260200180610cce6023913960400191505060405180910390fd5b610855838383610b03565b61089281604051806060016040528060268152602001610d13602691396001600160a01b038616600090815260208190526040902054919061091b565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546108c190826109b2565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156109aa5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561096f578181015183820152602001610957565b50505050905090810190601f16801561099c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015610a0c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b038216610a6e576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b610a7a60008383610b03565b600254610a8790826109b2565b6002556001600160a01b038216600090815260208190526040902054610aad90826109b2565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b80356001600160a01b03811681146104e157600080fd5b600060208284031215610b30578081fd5b610a0c82610b08565b60008060408385031215610b4b578081fd5b610b5483610b08565b9150610b6260208401610b08565b90509250929050565b600080600060608486031215610b7f578081fd5b610b8884610b08565b9250610b9660208501610b08565b9150604084013590509250925092565b60008060408385031215610bb8578182fd5b610bc183610b08565b946020939093013593505050565b600060208284031215610be0578081fd5b81518015158114610a0c578182fd5b600060208284031215610c00578081fd5b5035919050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b6000602080835283518082850152825b81811015610c8f57858101830151858201604001528201610c73565b81811115610ca05783604083870101525b50601f01601f1916929092016040019392505050565b90815260200190565b60ff9190911681526020019056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122069a94b4190fe4204521f185375175472fa777f0f59edf162489bbd1a5bdb764264736f6c6343000706003342616c616e636572203230555344432d3830544858204761756765204465706f736974", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101005760003560e01c8063546af3c311610097578063a457c2d711610066578063a457c2d7146101e8578063a9059cbb146101fb578063b6b55f251461020e578063dd62ed3e1461022157610100565b8063546af3c3146101b057806370a08231146101c557806382c63066146101d857806395d89b41146101e057610100565b806323b872dd116100d357806323b872dd146101605780632e1a7d4d14610173578063313ce56714610188578063395093511461019d57610100565b806306fdde0314610105578063095ea7b31461012357806317e280891461014357806318160ddd14610158575b600080fd5b61010d610234565b60405161011a9190610c63565b60405180910390f35b610136610131366004610ba6565b6102ca565b60405161011a9190610c58565b61014b6102e7565b60405161011a9190610cb6565b61014b6102f5565b61013661016e366004610b6b565b6102fb565b610186610181366004610bef565b610382565b005b61019061045c565b60405161011a9190610cbf565b6101366101ab366004610ba6565b610465565b6101b86104b3565b60405161011a9190610c07565b61014b6101d3366004610b1f565b6104c7565b6101b86104e6565b61010d6104fa565b6101366101f6366004610ba6565b61055b565b610136610209366004610ba6565b6105c3565b61018661021c366004610bef565b6105d7565b61014b61022f366004610b39565b6106a5565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102c05780601f10610295576101008083540402835291602001916102c0565b820191906000526020600020905b8154815290600101906020018083116102a357829003601f168201915b5050505050905090565b60006102de6102d76106d0565b84846106d4565b50600192915050565b697c1238ba7d2d4f8ef86190565b60025490565b60006103088484846107c0565b610378846103146106d0565b61037385604051806060016040528060288152602001610d39602891396001600160a01b038a166000908152600160205260408120906103526106d0565b6001600160a01b03168152602081019190915260400160002054919061091b565b6106d4565b5060019392505050565b60055460405163a9059cbb60e01b81526101009091046001600160a01b03169063a9059cbb906103b89033908590600401610c3f565b602060405180830381600087803b1580156103d257600080fd5b505af11580156103e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040a9190610bcf565b50610417336000836102fb565b50336001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364826040516104519190610cb6565b60405180910390a250565b60055460ff1690565b60006102de6104726106d0565b8461037385600160006104836106d0565b6001600160a01b03908116825260208083019390935260409182016000908120918c1681529252902054906109b2565b60055461010090046001600160a01b031681565b6001600160a01b0381166000908152602081905260409020545b919050565b60055461010090046001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102c05780601f10610295576101008083540402835291602001916102c0565b60006102de6105686106d0565b8461037385604051806060016040528060258152602001610daa60259139600160006105926106d0565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061091b565b60006102de6105d06106d0565b84846107c0565b6005546040516323b872dd60e01b81526101009091046001600160a01b0316906323b872dd9061060f90339030908690600401610c1b565b602060405180830381600087803b15801561062957600080fd5b505af115801561063d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106619190610bcf565b5061066c3382610a13565b336001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c826040516104519190610cb6565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166107195760405162461bcd60e51b8152600401808060200182810382526024815260200180610d866024913960400191505060405180910390fd5b6001600160a01b03821661075e5760405162461bcd60e51b8152600401808060200182810382526022815260200180610cf16022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166108055760405162461bcd60e51b8152600401808060200182810382526025815260200180610d616025913960400191505060405180910390fd5b6001600160a01b03821661084a5760405162461bcd60e51b8152600401808060200182810382526023815260200180610cce6023913960400191505060405180910390fd5b610855838383610b03565b61089281604051806060016040528060268152602001610d13602691396001600160a01b038616600090815260208190526040902054919061091b565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546108c190826109b2565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156109aa5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561096f578181015183820152602001610957565b50505050905090810190601f16801561099c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015610a0c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b038216610a6e576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b610a7a60008383610b03565b600254610a8790826109b2565b6002556001600160a01b038216600090815260208190526040902054610aad90826109b2565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b80356001600160a01b03811681146104e157600080fd5b600060208284031215610b30578081fd5b610a0c82610b08565b60008060408385031215610b4b578081fd5b610b5483610b08565b9150610b6260208401610b08565b90509250929050565b600080600060608486031215610b7f578081fd5b610b8884610b08565b9250610b9660208501610b08565b9150604084013590509250925092565b60008060408385031215610bb8578182fd5b610bc183610b08565b946020939093013593505050565b600060208284031215610be0578081fd5b81518015158114610a0c578182fd5b600060208284031215610c00578081fd5b5035919050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b6000602080835283518082850152825b81811015610c8f57858101830151858201604001528201610c73565b81811115610ca05783604083870101525b50601f01601f1916929092016040019392505050565b90815260200190565b60ff9190911681526020019056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122069a94b4190fe4204521f185375175472fa777f0f59edf162489bbd1a5bdb764264736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/BalancerGaugeController.json b/apps/api/src/app/hardhat/export/BalancerGaugeController.json new file mode 100644 index 000000000..ac3ffebdb --- /dev/null +++ b/apps/api/src/app/hardhat/export/BalancerGaugeController.json @@ -0,0 +1,26 @@ +{ + "abi": [ + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "gauge_relative_weight", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "" +} diff --git a/apps/api/src/app/hardhat/export/BalancerMinter.json b/apps/api/src/app/hardhat/export/BalancerMinter.json new file mode 100644 index 000000000..41f4bc83c --- /dev/null +++ b/apps/api/src/app/hardhat/export/BalancerMinter.json @@ -0,0 +1,73 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "BalancerMinter", + "sourceName": "contracts/mock/BalancerMinter.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "_balToken", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "balToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "gaugeMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "gauge", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60a060405234801561001057600080fd5b5060405161031e38038061031e83398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161028d61009160003960008181604b0152610183015261028d6000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806338d54645146100465780636a6278421461008a578063f74f5a75146100ab575b600080fd5b61006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61009d6100983660046101e9565b6100cb565b604051908152602001610081565b61009d6100b93660046101e9565b60006020819052908152604090205481565b60006100d782336100dd565b92915050565b6040516370a0823160e01b81526001600160a01b03828116600483015260009182918516906370a0823190602401602060405180830381865afa158015610128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014c9190610219565b9050600a61015a8183610232565b6040516340c10f1960e01b81526001600160a01b038681166004830152602482018390529194507f0000000000000000000000000000000000000000000000000000000000000000909116906340c10f1990604401600060405180830381600087803b1580156101c957600080fd5b505af11580156101dd573d6000803e3d6000fd5b50505050505092915050565b6000602082840312156101fb57600080fd5b81356001600160a01b038116811461021257600080fd5b9392505050565b60006020828403121561022b57600080fd5b5051919050565b80820281158282048414176100d757634e487b7160e01b600052601160045260246000fdfea26469706673582212202629fbd885bb989433d9b6bb75ed570fa59f837050fe85071e7afb8b8e38dc1d64736f6c63430008120033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806338d54645146100465780636a6278421461008a578063f74f5a75146100ab575b600080fd5b61006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61009d6100983660046101e9565b6100cb565b604051908152602001610081565b61009d6100b93660046101e9565b60006020819052908152604090205481565b60006100d782336100dd565b92915050565b6040516370a0823160e01b81526001600160a01b03828116600483015260009182918516906370a0823190602401602060405180830381865afa158015610128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014c9190610219565b9050600a61015a8183610232565b6040516340c10f1960e01b81526001600160a01b038681166004830152602482018390529194507f0000000000000000000000000000000000000000000000000000000000000000909116906340c10f1990604401600060405180830381600087803b1580156101c957600080fd5b505af11580156101dd573d6000803e3d6000fd5b50505050505092915050565b6000602082840312156101fb57600080fd5b81356001600160a01b038116811461021257600080fd5b9392505050565b60006020828403121561022b57600080fd5b5051919050565b80820281158282048414176100d757634e487b7160e01b600052601160045260246000fdfea26469706673582212202629fbd885bb989433d9b6bb75ed570fa59f837050fe85071e7afb8b8e38dc1d64736f6c63430008120033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/BalancerVault.json b/apps/api/src/app/hardhat/export/BalancerVault.json new file mode 100644 index 000000000..58d527d12 --- /dev/null +++ b/apps/api/src/app/hardhat/export/BalancerVault.json @@ -0,0 +1,121 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "BalancerVault", + "sourceName": "contracts/mock/BalancerVault.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_bpt", + "type": "address" + }, + { + "internalType": "address", + "name": "_usdc", + "type": "address" + }, + { + "internalType": "address", + "name": "_thx", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "bpt", + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "maxAmountsIn", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "fromInternalBalance", + "type": "bool" + } + ], + "internalType": "struct BalancerVault.JoinPoolRequest", + "name": "request", + "type": "tuple" + } + ], + "name": "joinPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "thx", + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "usdc", + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50604051610a7a380380610a7a83398101604081905261002f9161008d565b600080546001600160a01b039485166001600160a01b0319918216179091556001805493851693821693909317909255600280549190931691161790556100cf565b80516001600160a01b038116811461008857600080fd5b919050565b6000806000606084860312156100a1578283fd5b6100aa84610071565b92506100b860208501610071565b91506100c660408501610071565b90509250925092565b61099c806100de6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80633e413bee14610051578063546af3c31461006f578063a81d22e214610077578063b95cac281461007f575b600080fd5b610059610094565b604051610066919061089c565b60405180910390f35b6100596100a3565b6100596100b2565b61009261008d366004610751565b6100c1565b005b6001546001600160a01b031681565b6000546001600160a01b031681565b6002546001600160a01b031681565b600154602082015180516001600160a01b03909216916323b872dd9186913091906000906100eb57fe5b60200260200101516040518463ffffffff1660e01b81526004016101119392919061085f565b602060405180830381600087803b15801561012b57600080fd5b505af115801561013f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101639190610735565b50600254602082015180516001600160a01b03909216916323b872dd918691309190600190811061019057fe5b60200260200101516040518463ffffffff1660e01b81526004016101b69392919061085f565b602060405180830381600087803b1580156101d057600080fd5b505af11580156101e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102089190610735565b5060006102c1600160009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561025c57600080fd5b505afa158015610270573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610294919061083e565b60ff16600a0a83602001516000815181106102ab57fe5b60200260200101516104b990919063ffffffff16565b90506000610365600260009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561031657600080fd5b505afa15801561032a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061034e919061083e565b60ff16600a0a84602001516001815181106102ab57fe5b9050600061040660008054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103b857600080fd5b505afa1580156103cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f0919061083e565b60ff16600a0a6104008585610522565b90610583565b60005460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb906104399088908590600401610883565b602060405180830381600087803b15801561045357600080fd5b505af1158015610467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048b9190610735565b6104b05760405162461bcd60e51b81526004016104a7906108b0565b60405180910390fd5b50505050505050565b600080821161050f576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161051857fe5b0490505b92915050565b60008282018381101561057c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6000826105925750600061051c565b8282028284828161059f57fe5b041461057c5760405162461bcd60e51b81526004018080602001828103825260218152602001806109466021913960400191505060405180910390fd5b80356001600160a01b03811681146105f357600080fd5b919050565b600082601f830112610608578081fd5b8135602061061d61061883610916565b6108f2565b8281528181019085830183850287018401881015610639578586fd5b855b8581101561065e5761064c826105dc565b8452928401929084019060010161063b565b5090979650505050505050565b600082601f83011261067b578081fd5b8135602061068b61061883610916565b82815281810190858301838502870184018810156106a7578586fd5b855b8581101561065e578135845292840192908401906001016106a9565b80356105f381610934565b600082601f8301126106e0578081fd5b813567ffffffffffffffff8111156106f457fe5b610707601f8201601f19166020016108f2565b81815284602083860101111561071b578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215610746578081fd5b815161057c81610934565b60008060008060808587031215610766578283fd5b84359350610776602086016105dc565b9250610784604086016105dc565b9150606085013567ffffffffffffffff808211156107a0578283fd5b90860190608082890312156107b3578283fd5b6107bd60806108f2565b8235828111156107cb578485fd5b6107d78a8286016105f8565b8252506020830135828111156107eb578485fd5b6107f78a82860161066b565b60208301525060408301358281111561080e578485fd5b61081a8a8286016106d0565b60408301525061082c606084016106c5565b60608201529598949750929550505050565b60006020828403121561084f578081fd5b815160ff8116811461057c578182fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0391909116815260200190565b60208082526022908201527f42616c616e6365725661756c743a20425054207472616e73666572206661696c604082015261195960f21b606082015260800190565b60405181810167ffffffffffffffff8111828210171561090e57fe5b604052919050565b600067ffffffffffffffff82111561092a57fe5b5060209081020190565b801515811461094257600080fd5b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212200da6e89226c943a50bf8e722c06434037f552da74481e18a14dd1d7970f5716d64736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80633e413bee14610051578063546af3c31461006f578063a81d22e214610077578063b95cac281461007f575b600080fd5b610059610094565b604051610066919061089c565b60405180910390f35b6100596100a3565b6100596100b2565b61009261008d366004610751565b6100c1565b005b6001546001600160a01b031681565b6000546001600160a01b031681565b6002546001600160a01b031681565b600154602082015180516001600160a01b03909216916323b872dd9186913091906000906100eb57fe5b60200260200101516040518463ffffffff1660e01b81526004016101119392919061085f565b602060405180830381600087803b15801561012b57600080fd5b505af115801561013f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101639190610735565b50600254602082015180516001600160a01b03909216916323b872dd918691309190600190811061019057fe5b60200260200101516040518463ffffffff1660e01b81526004016101b69392919061085f565b602060405180830381600087803b1580156101d057600080fd5b505af11580156101e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102089190610735565b5060006102c1600160009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561025c57600080fd5b505afa158015610270573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610294919061083e565b60ff16600a0a83602001516000815181106102ab57fe5b60200260200101516104b990919063ffffffff16565b90506000610365600260009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561031657600080fd5b505afa15801561032a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061034e919061083e565b60ff16600a0a84602001516001815181106102ab57fe5b9050600061040660008054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103b857600080fd5b505afa1580156103cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f0919061083e565b60ff16600a0a6104008585610522565b90610583565b60005460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb906104399088908590600401610883565b602060405180830381600087803b15801561045357600080fd5b505af1158015610467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048b9190610735565b6104b05760405162461bcd60e51b81526004016104a7906108b0565b60405180910390fd5b50505050505050565b600080821161050f576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161051857fe5b0490505b92915050565b60008282018381101561057c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6000826105925750600061051c565b8282028284828161059f57fe5b041461057c5760405162461bcd60e51b81526004018080602001828103825260218152602001806109466021913960400191505060405180910390fd5b80356001600160a01b03811681146105f357600080fd5b919050565b600082601f830112610608578081fd5b8135602061061d61061883610916565b6108f2565b8281528181019085830183850287018401881015610639578586fd5b855b8581101561065e5761064c826105dc565b8452928401929084019060010161063b565b5090979650505050505050565b600082601f83011261067b578081fd5b8135602061068b61061883610916565b82815281810190858301838502870184018810156106a7578586fd5b855b8581101561065e578135845292840192908401906001016106a9565b80356105f381610934565b600082601f8301126106e0578081fd5b813567ffffffffffffffff8111156106f457fe5b610707601f8201601f19166020016108f2565b81815284602083860101111561071b578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215610746578081fd5b815161057c81610934565b60008060008060808587031215610766578283fd5b84359350610776602086016105dc565b9250610784604086016105dc565b9150606085013567ffffffffffffffff808211156107a0578283fd5b90860190608082890312156107b3578283fd5b6107bd60806108f2565b8235828111156107cb578485fd5b6107d78a8286016105f8565b8252506020830135828111156107eb578485fd5b6107f78a82860161066b565b60208301525060408301358281111561080e578485fd5b61081a8a8286016106d0565b60408301525061082c606084016106c5565b60608201529598949750929550505050565b60006020828403121561084f578081fd5b815160ff8116811461057c578182fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0391909116815260200190565b60208082526022908201527f42616c616e6365725661756c743a20425054207472616e73666572206661696c604082015261195960f21b606082015260800190565b60405181810167ffffffffffffffff8111828210171561090e57fe5b604052919050565b600067ffffffffffffffff82111561092a57fe5b5060209081020190565b801515811461094257600080fd5b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212200da6e89226c943a50bf8e722c06434037f552da74481e18a14dd1d7970f5716d64736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/CreateCall.json b/apps/api/src/app/hardhat/export/CreateCall.json new file mode 100644 index 000000000..b1e6d2346 --- /dev/null +++ b/apps/api/src/app/hardhat/export/CreateCall.json @@ -0,0 +1,77 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "CreateCall", + "sourceName": "@gnosis.pm/safe-contracts/contracts/libraries/CreateCall.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newContract", + "type": "address" + } + ], + "name": "ContractCreation", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "deploymentData", + "type": "bytes" + } + ], + "name": "performCreate", + "outputs": [ + { + "internalType": "address", + "name": "newContract", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "deploymentData", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "performCreate2", + "outputs": [ + { + "internalType": "address", + "name": "newContract", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610334806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80634847be6f1461003b5780634c8c9ea11461006a575b600080fd5b61004e610049366004610267565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b61004e6100783660046102b7565b610124565b60008183518460200186f590506001600160a01b0381166100e15760405162461bcd60e51b815260206004820152601960248201527810dbdd5b19081b9bdd0819195c1b1bde4818dbdb9d1c9858dd603a1b60448201526064015b60405180910390fd5b6040516001600160a01b03821681527f4db17dd5e4732fb6da34a148104a592783ca119a1e7bb8829eba6cbadef0b5119060200160405180910390a19392505050565b600081516020830184f090506001600160a01b0381166101825760405162461bcd60e51b815260206004820152601960248201527810dbdd5b19081b9bdd0819195c1b1bde4818dbdb9d1c9858dd603a1b60448201526064016100d8565b6040516001600160a01b03821681527f4db17dd5e4732fb6da34a148104a592783ca119a1e7bb8829eba6cbadef0b5119060200160405180910390a192915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126101eb57600080fd5b813567ffffffffffffffff80821115610206576102066101c4565b604051601f8301601f19908116603f0116810190828211818310171561022e5761022e6101c4565b8160405283815286602085880101111561024757600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561027c57600080fd5b83359250602084013567ffffffffffffffff81111561029a57600080fd5b6102a6868287016101da565b925050604084013590509250925092565b600080604083850312156102ca57600080fd5b82359150602083013567ffffffffffffffff8111156102e857600080fd5b6102f4858286016101da565b915050925092905056fea264697066735822122051feddc2dceae06ddb39e8e1b325167c8789615579f6ceb3a7d1a1238b4c744864736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80634847be6f1461003b5780634c8c9ea11461006a575b600080fd5b61004e610049366004610267565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b61004e6100783660046102b7565b610124565b60008183518460200186f590506001600160a01b0381166100e15760405162461bcd60e51b815260206004820152601960248201527810dbdd5b19081b9bdd0819195c1b1bde4818dbdb9d1c9858dd603a1b60448201526064015b60405180910390fd5b6040516001600160a01b03821681527f4db17dd5e4732fb6da34a148104a592783ca119a1e7bb8829eba6cbadef0b5119060200160405180910390a19392505050565b600081516020830184f090506001600160a01b0381166101825760405162461bcd60e51b815260206004820152601960248201527810dbdd5b19081b9bdd0819195c1b1bde4818dbdb9d1c9858dd603a1b60448201526064016100d8565b6040516001600160a01b03821681527f4db17dd5e4732fb6da34a148104a592783ca119a1e7bb8829eba6cbadef0b5119060200160405180910390a192915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126101eb57600080fd5b813567ffffffffffffffff80821115610206576102066101c4565b604051601f8301601f19908116603f0116810190828211818310171561022e5761022e6101c4565b8160405283815286602085880101111561024757600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561027c57600080fd5b83359250602084013567ffffffffffffffff81111561029a57600080fd5b6102a6868287016101da565b925050604084013590509250925092565b600080604083850312156102ca57600080fd5b82359150602083013567ffffffffffffffff8111156102e857600080fd5b6102f4858286016101da565b915050925092905056fea264697066735822122051feddc2dceae06ddb39e8e1b325167c8789615579f6ceb3a7d1a1238b4c744864736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/DefaultCallbackHandler.json b/apps/api/src/app/hardhat/export/DefaultCallbackHandler.json new file mode 100644 index 000000000..e683b3764 --- /dev/null +++ b/apps/api/src/app/hardhat/export/DefaultCallbackHandler.json @@ -0,0 +1,206 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DefaultCallbackHandler", + "sourceName": "@gnosis.pm/safe-contracts/contracts/handler/DefaultCallbackHandler.sol", + "abi": [ + { + "inputs": [], + "name": "NAME", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "tokensReceived", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610588806100206000396000f3fe608060405234801561001057600080fd5b506004361061007c5760003560e01c8063a3f4df7e1161005b578063a3f4df7e146100fb578063bc197c8114610144578063f23a6e6114610166578063ffa1ad741461018657600080fd5b806223de291461008157806301ffc9a71461009b578063150b7a02146100c3575b600080fd5b61009961008f366004610261565b5050505050505050565b005b6100ae6100a936600461030c565b6101aa565b60405190151581526020015b60405180910390f35b6100e26100d136600461033d565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020016100ba565b6101376040518060400160405280601881526020017f44656661756c742043616c6c6261636b2048616e646c6572000000000000000081525081565b6040516100ba91906103ac565b6100e2610152366004610440565b63bc197c8160e01b98975050505050505050565b6100e26101743660046104da565b63f23a6e6160e01b9695505050505050565b610137604051806040016040528060058152602001640312e302e360dc1b81525081565b60006001600160e01b03198216630271189760e51b14806101db57506001600160e01b03198216630a85bd0160e11b145b806101f657506001600160e01b031982166301ffc9a760e01b145b92915050565b80356001600160a01b038116811461021357600080fd5b919050565b60008083601f84011261022a57600080fd5b50813567ffffffffffffffff81111561024257600080fd5b60208301915083602082850101111561025a57600080fd5b9250929050565b60008060008060008060008060c0898b03121561027d57600080fd5b610286896101fc565b975061029460208a016101fc565b96506102a260408a016101fc565b955060608901359450608089013567ffffffffffffffff808211156102c657600080fd5b6102d28c838d01610218565b909650945060a08b01359150808211156102eb57600080fd5b506102f88b828c01610218565b999c989b5096995094979396929594505050565b60006020828403121561031e57600080fd5b81356001600160e01b03198116811461033657600080fd5b9392505050565b60008060008060006080868803121561035557600080fd5b61035e866101fc565b945061036c602087016101fc565b935060408601359250606086013567ffffffffffffffff81111561038f57600080fd5b61039b88828901610218565b969995985093965092949392505050565b60006020808352835180602085015260005b818110156103da578581018301518582016040015282016103be565b506000604082860101526040601f19601f8301168501019250505092915050565b60008083601f84011261040d57600080fd5b50813567ffffffffffffffff81111561042557600080fd5b6020830191508360208260051b850101111561025a57600080fd5b60008060008060008060008060a0898b03121561045c57600080fd5b610465896101fc565b975061047360208a016101fc565b9650604089013567ffffffffffffffff8082111561049057600080fd5b61049c8c838d016103fb565b909850965060608b01359150808211156104b557600080fd5b6104c18c838d016103fb565b909650945060808b01359150808211156102eb57600080fd5b60008060008060008060a087890312156104f357600080fd5b6104fc876101fc565b955061050a602088016101fc565b94506040870135935060608701359250608087013567ffffffffffffffff81111561053457600080fd5b61054089828a01610218565b979a969950949750929593949250505056fea2646970667358221220de19c10401b871e3bdbe864bca901a1383323ae0bca79d2027035d67f8ecb85064736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061007c5760003560e01c8063a3f4df7e1161005b578063a3f4df7e146100fb578063bc197c8114610144578063f23a6e6114610166578063ffa1ad741461018657600080fd5b806223de291461008157806301ffc9a71461009b578063150b7a02146100c3575b600080fd5b61009961008f366004610261565b5050505050505050565b005b6100ae6100a936600461030c565b6101aa565b60405190151581526020015b60405180910390f35b6100e26100d136600461033d565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020016100ba565b6101376040518060400160405280601881526020017f44656661756c742043616c6c6261636b2048616e646c6572000000000000000081525081565b6040516100ba91906103ac565b6100e2610152366004610440565b63bc197c8160e01b98975050505050505050565b6100e26101743660046104da565b63f23a6e6160e01b9695505050505050565b610137604051806040016040528060058152602001640312e302e360dc1b81525081565b60006001600160e01b03198216630271189760e51b14806101db57506001600160e01b03198216630a85bd0160e11b145b806101f657506001600160e01b031982166301ffc9a760e01b145b92915050565b80356001600160a01b038116811461021357600080fd5b919050565b60008083601f84011261022a57600080fd5b50813567ffffffffffffffff81111561024257600080fd5b60208301915083602082850101111561025a57600080fd5b9250929050565b60008060008060008060008060c0898b03121561027d57600080fd5b610286896101fc565b975061029460208a016101fc565b96506102a260408a016101fc565b955060608901359450608089013567ffffffffffffffff808211156102c657600080fd5b6102d28c838d01610218565b909650945060a08b01359150808211156102eb57600080fd5b506102f88b828c01610218565b999c989b5096995094979396929594505050565b60006020828403121561031e57600080fd5b81356001600160e01b03198116811461033657600080fd5b9392505050565b60008060008060006080868803121561035557600080fd5b61035e866101fc565b945061036c602087016101fc565b935060408601359250606086013567ffffffffffffffff81111561038f57600080fd5b61039b88828901610218565b969995985093965092949392505050565b60006020808352835180602085015260005b818110156103da578581018301518582016040015282016103be565b506000604082860101526040601f19601f8301168501019250505092915050565b60008083601f84011261040d57600080fd5b50813567ffffffffffffffff81111561042557600080fd5b6020830191508360208260051b850101111561025a57600080fd5b60008060008060008060008060a0898b03121561045c57600080fd5b610465896101fc565b975061047360208a016101fc565b9650604089013567ffffffffffffffff8082111561049057600080fd5b61049c8c838d016103fb565b909850965060608b01359150808211156104b557600080fd5b6104c18c838d016103fb565b909650945060808b01359150808211156102eb57600080fd5b60008060008060008060a087890312156104f357600080fd5b6104fc876101fc565b955061050a602088016101fc565b94506040870135935060608701359250608087013567ffffffffffffffff81111561053457600080fd5b61054089828a01610218565b979a969950949750929593949250505056fea2646970667358221220de19c10401b871e3bdbe864bca901a1383323ae0bca79d2027035d67f8ecb85064736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/GnosisSafeL2.json b/apps/api/src/app/hardhat/export/GnosisSafeL2.json new file mode 100644 index 000000000..859225a6f --- /dev/null +++ b/apps/api/src/app/hardhat/export/GnosisSafeL2.json @@ -0,0 +1,1147 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "GnosisSafeL2", + "sourceName": "@gnosis.pm/safe-contracts/contracts/GnosisSafeL2.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "AddedOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "approvedHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ApproveHash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "handler", + "type": "address" + } + ], + "name": "ChangedFallbackHandler", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "guard", + "type": "address" + } + ], + "name": "ChangedGuard", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + } + ], + "name": "ChangedThreshold", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "DisabledModule", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "EnabledModule", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + } + ], + "name": "ExecutionFailure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "ExecutionFromModuleFailure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "ExecutionFromModuleSuccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + } + ], + "name": "ExecutionSuccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "RemovedOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "module", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + } + ], + "name": "SafeModuleTransaction", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "safeTxGas", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "baseGas", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "gasToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address payable", + "name": "refundReceiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "signatures", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "additionalInfo", + "type": "bytes" + } + ], + "name": "SafeMultiSigTransaction", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "owners", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "initializer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "fallbackHandler", + "type": "address" + } + ], + "name": "SafeSetup", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "SignMsg", + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "fallback" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "addOwnerWithThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hashToApprove", + "type": "bytes32" + } + ], + "name": "approveHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "approvedHashes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "changeThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "dataHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signatures", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "requiredSignatures", + "type": "uint256" + } + ], + "name": "checkNSignatures", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "dataHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signatures", + "type": "bytes" + } + ], + "name": "checkSignatures", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "prevModule", + "type": "address" + }, + { + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "disableModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "enableModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "safeTxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gasToken", + "type": "address" + }, + { + "internalType": "address", + "name": "refundReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + } + ], + "name": "encodeTransactionData", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "safeTxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gasToken", + "type": "address" + }, + { + "internalType": "address payable", + "name": "refundReceiver", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signatures", + "type": "bytes" + } + ], + "name": "execTransaction", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + } + ], + "name": "execTransactionFromModule", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + } + ], + "name": "execTransactionFromModuleReturnData", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "start", + "type": "address" + }, + { + "internalType": "uint256", + "name": "pageSize", + "type": "uint256" + } + ], + "name": "getModulesPaginated", + "outputs": [ + { + "internalType": "address[]", + "name": "array", + "type": "address[]" + }, + { + "internalType": "address", + "name": "next", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOwners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "offset", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "getStorageAt", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "safeTxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gasToken", + "type": "address" + }, + { + "internalType": "address", + "name": "refundReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + } + ], + "name": "getTransactionHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "module", + "type": "address" + } + ], + "name": "isModuleEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "prevOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "removeOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + } + ], + "name": "requiredTxGas", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "handler", + "type": "address" + } + ], + "name": "setFallbackHandler", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "guard", + "type": "address" + } + ], + "name": "setGuard", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_owners", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "address", + "name": "fallbackHandler", + "type": "address" + }, + { + "internalType": "address", + "name": "paymentToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address payable", + "name": "paymentReceiver", + "type": "address" + } + ], + "name": "setup", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "signedMessages", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "targetContract", + "type": "address" + }, + { + "internalType": "bytes", + "name": "calldataPayload", + "type": "bytes" + } + ], + "name": "simulateAndRevert", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "prevOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "swapOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5060016004556131be806100256000396000f3fe6080604052600436106101dc5760003560e01c8063affed0e011610102578063e19a9dd911610095578063f08a032311610064578063f08a032314610620578063f698da2514610640578063f8dc5dd914610655578063ffa1ad741461067557610218565b8063e19a9dd9146105ab578063e318b52b146105cb578063e75235b8146105eb578063e86637db1461060057610218565b8063cc2f8452116100d1578063cc2f84521461051d578063d4d9bdcd1461054b578063d8d11f781461056b578063e009cfde1461058b57610218565b8063affed0e0146104a7578063b4faba09146104bd578063b63e800d146104dd578063c4ca3a9c146104fd57610218565b80635624b25b1161017a5780636a761202116101495780636a7612021461041a5780637d8329741461042d578063934f3a1114610465578063a0e67e2b1461048557610218565b80635624b25b146103805780635ae6bd37146103ad578063610b5925146103da578063694e80c3146103fa57610218565b80632f54bf6e116101b65780632f54bf6e146102f55780633408e47014610315578063468721a7146103325780635229073f1461035257610218565b80630d582f131461027e57806312fb68e0146102a05780632d9ad53d146102c057610218565b366102185760405134815233907f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d9060200160405180910390a2005b34801561022457600080fd5b507f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d580548061024f57005b36600080373360601b365260008060143601600080855af190503d6000803e80610278573d6000fd5b503d6000f35b34801561028a57600080fd5b5061029e610299366004612581565b6106a6565b005b3480156102ac57600080fd5b5061029e6102bb36600461264f565b610806565b3480156102cc57600080fd5b506102e06102db3660046126c3565b610c68565b60405190151581526020015b60405180910390f35b34801561030157600080fd5b506102e06103103660046126c3565b610ca3565b34801561032157600080fd5b50465b6040519081526020016102ec565b34801561033e57600080fd5b506102e061034d3660046126ef565b610cdb565b34801561035e57600080fd5b5061037261036d3660046126ef565b610d31565b6040516102ec92919061279e565b34801561038c57600080fd5b506103a061039b3660046127b9565b610d67565b6040516102ec91906127db565b3480156103b957600080fd5b506103246103c83660046127ee565b60076020526000908152604090205481565b3480156103e657600080fd5b5061029e6103f53660046126c3565b610de2565b34801561040657600080fd5b5061029e6104153660046127ee565b610f24565b6102e061042836600461284f565b610fbc565b34801561043957600080fd5b50610324610448366004612581565b600860209081526000928352604080842090915290825290205481565b34801561047157600080fd5b5061029e610480366004612927565b611064565b34801561049157600080fd5b5061049a6110ae565b6040516102ec91906129d8565b3480156104b357600080fd5b5061032460055481565b3480156104c957600080fd5b5061029e6104d83660046129eb565b61119e565b3480156104e957600080fd5b5061029e6104f8366004612a3a565b6111c1565b34801561050957600080fd5b50610324610518366004612b2e565b6112e2565b34801561052957600080fd5b5061053d610538366004612581565b61137c565b6040516102ec929190612b9e565b34801561055757600080fd5b5061029e6105663660046127ee565b611475565b34801561057757600080fd5b50610324610586366004612bc8565b61150a565b34801561059757600080fd5b5061029e6105a6366004612c88565b611537565b3480156105b757600080fd5b5061029e6105c63660046126c3565b611666565b3480156105d757600080fd5b5061029e6105e6366004612cc1565b6116cb565b3480156105f757600080fd5b50600454610324565b34801561060c57600080fd5b506103a061061b366004612bc8565b6118ba565b34801561062c57600080fd5b5061029e61063b3660046126c3565b611993565b34801561064c57600080fd5b506103246119fc565b34801561066157600080fd5b5061029e610670366004612d0c565b611a53565b34801561068157600080fd5b506103a0604051806040016040528060058152602001640312e332e360dc1b81525081565b6106ae611bc6565b6001600160a01b038216158015906106d057506001600160a01b038216600114155b80156106e557506001600160a01b0382163014155b61070a5760405162461bcd60e51b815260040161070190612d4d565b60405180910390fd5b6001600160a01b0382811660009081526002602052604090205416156107425760405162461bcd60e51b815260040161070190612d6c565b60026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080546001600160a01b038481166000818152604081208054939094166001600160a01b0319938416179093556001835283549091161790915560038054916107af83612da1565b90915550506040516001600160a01b03831681527f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea269060200160405180910390a180600454146108025761080281610f24565b5050565b610811816041611bff565b825110156108495760405162461bcd60e51b8152602060048201526005602482015264047533032360dc1b6044820152606401610701565b6000808060008060005b86811015610c5c576041818102890160208101516040820151919092015160ff16955090935091506000849003610a24579193508391610894876041611bff565b8210156108cb5760405162461bcd60e51b8152602060048201526005602482015264475330323160d81b6044820152606401610701565b87516108d8836020611c3b565b111561090e5760405162461bcd60e51b815260206004820152600560248201526423a998191960d91b6044820152606401610701565b60208289018101518951909161093190839061092b908790611c3b565b90611c3b565b11156109675760405162461bcd60e51b8152602060048201526005602482015264475330323360d81b6044820152606401610701565b6040516320c13b0b60e01b8082528a8501602001916001600160a01b038916906320c13b0b9061099d908f908690600401612dba565b602060405180830381865afa1580156109ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109de9190612ddf565b6001600160e01b03191614610a1d5760405162461bcd60e51b815260206004820152600560248201526411d4cc0c8d60da1b6044820152606401610701565b5050610bcb565b8360ff16600103610aa6579193508391336001600160a01b0384161480610a6d57506001600160a01b03851660009081526008602090815260408083208d845290915290205415155b610aa15760405162461bcd60e51b8152602060048201526005602482015264475330323560d81b6044820152606401610701565b610bcb565b601e8460ff161115610b6b576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018b9052600190605c0160405160208183030381529060405280519060200120600486610b0b9190612e09565b6040805160008152602081018083529390935260ff90911690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610b5a573d6000803e3d6000fd5b505050602060405103519450610bcb565b6040805160008152602081018083528c905260ff861691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa158015610bbe573d6000803e3d6000fd5b5050506020604051035194505b856001600160a01b0316856001600160a01b0316118015610c0557506001600160a01b038581166000908152600260205260409020541615155b8015610c1b57506001600160a01b038516600114155b610c4f5760405162461bcd60e51b815260206004820152600560248201526423a998191b60d91b6044820152606401610701565b9394508493600101610853565b50505050505050505050565b600060016001600160a01b03831614801590610c9d57506001600160a01b038281166000908152600160205260409020541615155b92915050565b60006001600160a01b038216600114801590610c9d5750506001600160a01b0390811660009081526002602052604090205416151590565b60007fb648d3644f584ed1c2232d53c46d87e693586486ad0d1175f8656013110b714e3386868686604051610d14959493929190612e5a565b60405180910390a1610d2885858585611c57565b95945050505050565b60006060610d4186868686610cdb565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b60606000610d76836020612ea6565b6001600160401b03811115610d8d57610d8d6125ad565b6040519080825280601f01601f191660200182016040528015610db7576020820181803683370190505b50905060005b83811015610dda5784810154602080830284010152600101610dbd565b509392505050565b610dea611bc6565b6001600160a01b03811615801590610e0c57506001600160a01b038116600114155b610e405760405162461bcd60e51b8152602060048201526005602482015264475331303160d81b6044820152606401610701565b6001600160a01b038181166000908152600160205260409020541615610e905760405162461bcd60e51b815260206004820152600560248201526423a998981960d91b6044820152606401610701565b600160208181527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f80546001600160a01b03858116600081815260408082208054949095166001600160a01b031994851617909455959095528254168417909155519182527fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844091015b60405180910390a150565b610f2c611bc6565b600354811115610f4e5760405162461bcd60e51b815260040161070190612ebd565b6001811015610f875760405162461bcd60e51b815260206004820152600560248201526423a999181960d91b6044820152606401610701565b60048190556040518181527f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c9390602001610f19565b600554600454604080516020810193909352339083015260608281019190915260009160800160405160208183030381529060405290507f66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed8d8d8d8d8d8d8d8d8d8d8d8c6040516110389c9b9a99989796959493929190612f05565b60405180910390a16110538d8d8d8d8d8d8d8d8d8d8d611d2e565b9d9c50505050505050505050505050565b6004548061109c5760405162461bcd60e51b8152602060048201526005602482015264475330303160d81b6044820152606401610701565b6110a884848484610806565b50505050565b606060006003546001600160401b038111156110cc576110cc6125ad565b6040519080825280602002602001820160405280156110f5578160200160208202803683370190505b506001600090815260026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e054919250906001600160a01b03165b6001600160a01b038116600114611196578083838151811061115657611156612f9c565b6001600160a01b0392831660209182029290920181019190915291811660009081526002909252604090912054168161118e81612da1565b925050611132565b509092915050565b600080825160208401855af480600052503d6020523d600060403e60403d016000fd5b6111ff8a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612077915050565b6001600160a01b0384161561123657611236847f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d555565b6112768787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061225392505050565b811561128d5761128b8260006001868561234d565b505b336001600160a01b03167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b896040516112ce959493929190612fb2565b60405180910390a250505050505050505050565b6000805a905061132b878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525089925050505a612453565b61133457600080fd5b60005a611341908361301e565b90508060405160200161135691815260200190565b60408051601f198184030181529082905262461bcd60e51b8252610701916004016127db565b60606000826001600160401b03811115611398576113986125ad565b6040519080825280602002602001820160405280156113c1578160200160208202803683370190505b506001600160a01b0380861660009081526001602052604081205492945091165b6001600160a01b0381161580159061140457506001600160a01b038116600114155b801561140f57508482105b15611467578084838151811061142757611427612f9c565b6001600160a01b0392831660209182029290920181019190915291811660009081526001909252604090912054168161145f81612da1565b9250506113e2565b908352919491935090915050565b336000908152600260205260409020546001600160a01b03166114c25760405162461bcd60e51b8152602060048201526005602482015264047533033360dc1b6044820152606401610701565b336000818152600860209081526040808320858452909152808220600190555183917ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c91a350565b600061151f8c8c8c8c8c8c8c8c8c8c8c6118ba565b8051906020012090509b9a5050505050505050505050565b61153f611bc6565b6001600160a01b0381161580159061156157506001600160a01b038116600114155b6115955760405162461bcd60e51b8152602060048201526005602482015264475331303160d81b6044820152606401610701565b6001600160a01b038281166000908152600160205260409020548116908216146115e95760405162461bcd60e51b8152602060048201526005602482015264475331303360d81b6044820152606401610701565b6001600160a01b038181166000818152600160209081526040808320805488871685528285208054919097166001600160a01b03199182161790965592849052825490941690915591519081527faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427691015b60405180910390a15050565b61166e611bc6565b7f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c88181556040516001600160a01b03831681527f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa29060200161165a565b6116d3611bc6565b6001600160a01b038116158015906116f557506001600160a01b038116600114155b801561170a57506001600160a01b0381163014155b6117265760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b03818116600090815260026020526040902054161561175e5760405162461bcd60e51b815260040161070190612d6c565b6001600160a01b0382161580159061178057506001600160a01b038216600114155b61179c5760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b038381166000908152600260205260409020548116908316146117f05760405162461bcd60e51b8152602060048201526005602482015264475332303560d81b6044820152606401610701565b6001600160a01b038281166000818152600260209081526040808320805487871680865283862080549289166001600160a01b0319938416179055968a1685528285208054821690971790965592849052825490941690915591519081527ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf910160405180910390a16040516001600160a01b03821681527f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea269060200160405180910390a1505050565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d6040516118f4929190613031565b60405190819003812061191a949392918e908e908e908e908e908e908e90602001613041565b60408051601f1981840301815291905280516020909101209050601960f81b600160f81b6119466119fc565b6040516001600160f81b031993841660208201529290911660218301526022820152604281018290526062016040516020818303038152906040529150509b9a5050505050505050505050565b61199b611bc6565b6119c3817f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d555565b6040516001600160a01b03821681527f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b090602001610f19565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692184660408051602081019390935282015230606082015260800160405160208183030381529060405280519060200120905090565b611a5b611bc6565b806001600354611a6b919061301e565b1015611a895760405162461bcd60e51b815260040161070190612ebd565b6001600160a01b03821615801590611aab57506001600160a01b038216600114155b611ac75760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b03838116600090815260026020526040902054811690831614611b1b5760405162461bcd60e51b8152602060048201526005602482015264475332303560d81b6044820152606401610701565b6001600160a01b03828116600081815260026020526040808220805488861684529183208054929095166001600160a01b03199283161790945591815282549091169091556003805491611b6e836130b0565b90915550506040516001600160a01b03831681527ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf9060200160405180910390a18060045414611bc157611bc181610f24565b505050565b333014611bfd5760405162461bcd60e51b8152602060048201526005602482015264475330333160d81b6044820152606401610701565b565b600082600003611c1157506000610c9d565b6000611c1d8385612ea6565b905082611c2a85836130c7565b14611c3457600080fd5b9392505050565b600080611c4883856130e9565b905083811015611c3457600080fd5b600033600114801590611c815750336000908152600160205260409020546001600160a01b031615155b611cb55760405162461bcd60e51b815260206004820152600560248201526411d4cc4c0d60da1b6044820152606401610701565b611cc2858585855a612453565b90508015611cfa5760405133907f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb890600090a2611d26565b60405133907facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37590600090a25b949350505050565b6000806000611d488e8e8e8e8e8e8e8e8e8e6005546118ba565b600580549192506000611d5a83612da1565b9091555050805160208201209150611d73828286611064565b506000611d9e7f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c85490565b90506001600160a01b03811615611e2457806001600160a01b03166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401611df19c9b9a999897969594939291906130fc565b600060405180830381600087803b158015611e0b57600080fd5b505af1158015611e1f573d6000803e3d6000fd5b505050505b611e50611e338a6109c46130e9565b603f611e408c6040612ea6565b611e4a91906130c7565b90612498565b611e5c906101f46130e9565b5a1015611e935760405162461bcd60e51b8152602060048201526005602482015264047533031360dc1b6044820152606401610701565b60005a9050611f048f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e8c600014611ef1578e612453565b6109c45a611eff919061301e565b612453565b9350611f115a82906124af565b90508380611f1e57508915155b80611f2857508715155b611f5c5760405162461bcd60e51b8152602060048201526005602482015264475330313360d81b6044820152606401610701565b60008815611f7457611f71828b8b8b8b61234d565b90505b8415611fb85760408051858152602081018390527f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e910160405180910390a1611ff2565b60408051858152602081018390527f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d23910160405180910390a15b50506001600160a01b0381161561206657604051631264e26d60e31b81526004810183905283151560248201526001600160a01b03821690639327136890604401600060405180830381600087803b15801561204d57600080fd5b505af1158015612061573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b600454156120af5760405162461bcd60e51b8152602060048201526005602482015264047533230360dc1b6044820152606401610701565b81518111156120d05760405162461bcd60e51b815260040161070190612ebd565b60018110156121095760405162461bcd60e51b815260206004820152600560248201526423a999181960d91b6044820152606401610701565b600160005b835181101561222057600084828151811061212b5761212b612f9c565b6020026020010151905060006001600160a01b0316816001600160a01b03161415801561216257506001600160a01b038116600114155b801561217757506001600160a01b0381163014155b80156121955750806001600160a01b0316836001600160a01b031614155b6121b15760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b0381811660009081526002602052604090205416156121e95760405162461bcd60e51b815260040161070190612d6c565b6001600160a01b03928316600090815260026020526040902080546001600160a01b0319169382169390931790925560010161210e565b506001600160a01b0316600090815260026020526040902080546001600160a01b03191660011790559051600355600455565b600160008190526020527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f546001600160a01b0316156122bd5760405162461bcd60e51b8152602060048201526005602482015264047533130360dc1b6044820152606401610701565b6001600081905260208190527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f80546001600160a01b03191690911790556001600160a01b03821615610802576123198260008360015a612453565b6108025760405162461bcd60e51b8152602060048201526005602482015264047533030360dc1b6044820152606401610701565b6000806001600160a01b038316156123655782612367565b325b90506001600160a01b0384166123fa576123993a8610612387573a612389565b855b6123938989611c3b565b90611bff565b6040519092506001600160a01b0382169083156108fc029084906000818181858888f193505050506123f55760405162461bcd60e51b8152602060048201526005602482015264475330313160d81b6044820152606401610701565b612449565b612408856123938989611c3b565b91506124158482846124ca565b6124495760405162461bcd60e51b815260206004820152600560248201526423a998189960d91b6044820152606401610701565b5095945050505050565b6000600183600181111561246957612469612e22565b03612481576000808551602087018986f49050610d28565b600080855160208701888a87f19695505050505050565b6000818310156124a85781611c34565b5090919050565b6000828211156124be57600080fd5b6000611d26838561301e565b604080516001600160a01b03841660248201526044808201849052825180830390910181526064909101909152602080820180516001600160e01b031663a9059cbb60e01b1781528251600093929184919082896127105a03f13d801561253c5760208114612544576000935061254f565b81935061254f565b600051158215171593505b5050509392505050565b6001600160a01b038116811461256e57600080fd5b50565b803561257c81612559565b919050565b6000806040838503121561259457600080fd5b823561259f81612559565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126125d457600080fd5b81356001600160401b03808211156125ee576125ee6125ad565b604051601f8301601f19908116603f01168101908282118183101715612616576126166125ad565b8160405283815286602085880101111561262f57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561266557600080fd5b8435935060208501356001600160401b038082111561268357600080fd5b61268f888389016125c3565b945060408701359150808211156126a557600080fd5b506126b2878288016125c3565b949793965093946060013593505050565b6000602082840312156126d557600080fd5b8135611c3481612559565b80356002811061257c57600080fd5b6000806000806080858703121561270557600080fd5b843561271081612559565b93506020850135925060408501356001600160401b0381111561273257600080fd5b61273e878288016125c3565b92505061274d606086016126e0565b905092959194509250565b6000815180845260005b8181101561277e57602081850181015186830182015201612762565b506000602082860101526020601f19601f83011685010191505092915050565b8215158152604060208201526000611d266040830184612758565b600080604083850312156127cc57600080fd5b50508035926020909101359150565b602081526000611c346020830184612758565b60006020828403121561280057600080fd5b5035919050565b60008083601f84011261281957600080fd5b5081356001600160401b0381111561283057600080fd5b60208301915083602082850101111561284857600080fd5b9250929050565b60008060008060008060008060008060006101408c8e03121561287157600080fd5b61287a8c612571565b9a5060208c013599506001600160401b038060408e0135111561289c57600080fd5b6128ac8e60408f01358f01612807565b909a5098506128bd60608e016126e0565b975060808d0135965060a08d0135955060c08d013594506128e060e08e01612571565b93506128ef6101008e01612571565b9250806101208e0135111561290357600080fd5b506129158d6101208e01358e016125c3565b90509295989b509295989b9093969950565b60008060006060848603121561293c57600080fd5b8335925060208401356001600160401b038082111561295a57600080fd5b612966878388016125c3565b9350604086013591508082111561297c57600080fd5b50612989868287016125c3565b9150509250925092565b60008151808452602080850194506020840160005b838110156129cd5781516001600160a01b0316875295820195908201906001016129a8565b509495945050505050565b602081526000611c346020830184612993565b600080604083850312156129fe57600080fd5b8235612a0981612559565b915060208301356001600160401b03811115612a2457600080fd5b612a30858286016125c3565b9150509250929050565b6000806000806000806000806000806101008b8d031215612a5a57600080fd5b8a356001600160401b0380821115612a7157600080fd5b818d0191508d601f830112612a8557600080fd5b813581811115612a9457600080fd5b8e60208260051b8501011115612aa957600080fd5b60208381019d50909b508d01359950612ac460408e01612571565b985060608d0135915080821115612ada57600080fd5b50612ae78d828e01612807565b9097509550612afa905060808c01612571565b9350612b0860a08c01612571565b925060c08b01359150612b1d60e08c01612571565b90509295989b9194979a5092959850565b600080600080600060808688031215612b4657600080fd5b8535612b5181612559565b94506020860135935060408601356001600160401b03811115612b7357600080fd5b612b7f88828901612807565b9094509250612b929050606087016126e0565b90509295509295909350565b604081526000612bb16040830185612993565b905060018060a01b03831660208301529392505050565b60008060008060008060008060008060006101408c8e031215612bea57600080fd5b8b35612bf581612559565b9a5060208c0135995060408c01356001600160401b03811115612c1757600080fd5b612c238e828f01612807565b909a509850612c36905060608d016126e0565b965060808c0135955060a08c0135945060c08c0135935060e08c0135612c5b81612559565b92506101008c0135612c6c81612559565b809250506101208c013590509295989b509295989b9093969950565b60008060408385031215612c9b57600080fd5b8235612ca681612559565b91506020830135612cb681612559565b809150509250929050565b600080600060608486031215612cd657600080fd5b8335612ce181612559565b92506020840135612cf181612559565b91506040840135612d0181612559565b809150509250925092565b600080600060608486031215612d2157600080fd5b8335612d2c81612559565b92506020840135612d3c81612559565b929592945050506040919091013590565b602080825260059082015264475332303360d81b604082015260600190565b60208082526005908201526411d4cc8c0d60da1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201612db357612db3612d8b565b5060010190565b604081526000612dcd6040830185612758565b8281036020840152610d288185612758565b600060208284031215612df157600080fd5b81516001600160e01b031981168114611c3457600080fd5b60ff8281168282160390811115610c9d57610c9d612d8b565b634e487b7160e01b600052602160045260246000fd5b60028110612e5657634e487b7160e01b600052602160045260246000fd5b9052565b6001600160a01b038681168252851660208201526040810184905260a060608201819052600090612e8d90830185612758565b9050612e9c6080830184612e38565b9695505050505050565b8082028115828204841417610c9d57610c9d612d8b565b602080825260059082015264475332303160d81b604082015260600190565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600060018060a01b03808f1683528d60208401526101606040840152612f3061016084018d8f612edc565b612f3d606085018d612e38565b8a60808501528960a08501528860c085015281881660e0850152818716610100850152838103610120850152612f738187612758565b915050828103610140840152612f898185612758565b9f9e505050505050505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6080808252810185905260008660a08301825b88811015612ff5578235612fd881612559565b6001600160a01b0316825260209283019290910190600101612fc5565b50602084019690965250506001600160a01b039283166040820152911660609091015292915050565b81810381811115610c9d57610c9d612d8b565b8183823760009101908152919050565b8b81526001600160a01b038b81166020830152604082018b9052606082018a9052610160820190613075608084018b612e38565b60a083019890985260c082019690965260e0810194909452918516610100840152909316610120820152610140019190915295945050505050565b6000816130bf576130bf612d8b565b506000190190565b6000826130e457634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610c9d57610c9d612d8b565b600060018060a01b03808f1683528d6020840152610160604084015261312761016084018d8f612edc565b613134606085018d612e38565b8a60808501528960a08501528860c085015281881660e085015281871661010085015283810361012085015261316a8187612758565b925050808416610140840152509d9c5050505050505050505050505056fea2646970667358221220976dca493dd95f9b904ec290061e789606e429364fc397e8d8017c4baf11a91664736f6c63430008180033", + "deployedBytecode": "0x6080604052600436106101dc5760003560e01c8063affed0e011610102578063e19a9dd911610095578063f08a032311610064578063f08a032314610620578063f698da2514610640578063f8dc5dd914610655578063ffa1ad741461067557610218565b8063e19a9dd9146105ab578063e318b52b146105cb578063e75235b8146105eb578063e86637db1461060057610218565b8063cc2f8452116100d1578063cc2f84521461051d578063d4d9bdcd1461054b578063d8d11f781461056b578063e009cfde1461058b57610218565b8063affed0e0146104a7578063b4faba09146104bd578063b63e800d146104dd578063c4ca3a9c146104fd57610218565b80635624b25b1161017a5780636a761202116101495780636a7612021461041a5780637d8329741461042d578063934f3a1114610465578063a0e67e2b1461048557610218565b80635624b25b146103805780635ae6bd37146103ad578063610b5925146103da578063694e80c3146103fa57610218565b80632f54bf6e116101b65780632f54bf6e146102f55780633408e47014610315578063468721a7146103325780635229073f1461035257610218565b80630d582f131461027e57806312fb68e0146102a05780632d9ad53d146102c057610218565b366102185760405134815233907f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d9060200160405180910390a2005b34801561022457600080fd5b507f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d580548061024f57005b36600080373360601b365260008060143601600080855af190503d6000803e80610278573d6000fd5b503d6000f35b34801561028a57600080fd5b5061029e610299366004612581565b6106a6565b005b3480156102ac57600080fd5b5061029e6102bb36600461264f565b610806565b3480156102cc57600080fd5b506102e06102db3660046126c3565b610c68565b60405190151581526020015b60405180910390f35b34801561030157600080fd5b506102e06103103660046126c3565b610ca3565b34801561032157600080fd5b50465b6040519081526020016102ec565b34801561033e57600080fd5b506102e061034d3660046126ef565b610cdb565b34801561035e57600080fd5b5061037261036d3660046126ef565b610d31565b6040516102ec92919061279e565b34801561038c57600080fd5b506103a061039b3660046127b9565b610d67565b6040516102ec91906127db565b3480156103b957600080fd5b506103246103c83660046127ee565b60076020526000908152604090205481565b3480156103e657600080fd5b5061029e6103f53660046126c3565b610de2565b34801561040657600080fd5b5061029e6104153660046127ee565b610f24565b6102e061042836600461284f565b610fbc565b34801561043957600080fd5b50610324610448366004612581565b600860209081526000928352604080842090915290825290205481565b34801561047157600080fd5b5061029e610480366004612927565b611064565b34801561049157600080fd5b5061049a6110ae565b6040516102ec91906129d8565b3480156104b357600080fd5b5061032460055481565b3480156104c957600080fd5b5061029e6104d83660046129eb565b61119e565b3480156104e957600080fd5b5061029e6104f8366004612a3a565b6111c1565b34801561050957600080fd5b50610324610518366004612b2e565b6112e2565b34801561052957600080fd5b5061053d610538366004612581565b61137c565b6040516102ec929190612b9e565b34801561055757600080fd5b5061029e6105663660046127ee565b611475565b34801561057757600080fd5b50610324610586366004612bc8565b61150a565b34801561059757600080fd5b5061029e6105a6366004612c88565b611537565b3480156105b757600080fd5b5061029e6105c63660046126c3565b611666565b3480156105d757600080fd5b5061029e6105e6366004612cc1565b6116cb565b3480156105f757600080fd5b50600454610324565b34801561060c57600080fd5b506103a061061b366004612bc8565b6118ba565b34801561062c57600080fd5b5061029e61063b3660046126c3565b611993565b34801561064c57600080fd5b506103246119fc565b34801561066157600080fd5b5061029e610670366004612d0c565b611a53565b34801561068157600080fd5b506103a0604051806040016040528060058152602001640312e332e360dc1b81525081565b6106ae611bc6565b6001600160a01b038216158015906106d057506001600160a01b038216600114155b80156106e557506001600160a01b0382163014155b61070a5760405162461bcd60e51b815260040161070190612d4d565b60405180910390fd5b6001600160a01b0382811660009081526002602052604090205416156107425760405162461bcd60e51b815260040161070190612d6c565b60026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080546001600160a01b038481166000818152604081208054939094166001600160a01b0319938416179093556001835283549091161790915560038054916107af83612da1565b90915550506040516001600160a01b03831681527f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea269060200160405180910390a180600454146108025761080281610f24565b5050565b610811816041611bff565b825110156108495760405162461bcd60e51b8152602060048201526005602482015264047533032360dc1b6044820152606401610701565b6000808060008060005b86811015610c5c576041818102890160208101516040820151919092015160ff16955090935091506000849003610a24579193508391610894876041611bff565b8210156108cb5760405162461bcd60e51b8152602060048201526005602482015264475330323160d81b6044820152606401610701565b87516108d8836020611c3b565b111561090e5760405162461bcd60e51b815260206004820152600560248201526423a998191960d91b6044820152606401610701565b60208289018101518951909161093190839061092b908790611c3b565b90611c3b565b11156109675760405162461bcd60e51b8152602060048201526005602482015264475330323360d81b6044820152606401610701565b6040516320c13b0b60e01b8082528a8501602001916001600160a01b038916906320c13b0b9061099d908f908690600401612dba565b602060405180830381865afa1580156109ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109de9190612ddf565b6001600160e01b03191614610a1d5760405162461bcd60e51b815260206004820152600560248201526411d4cc0c8d60da1b6044820152606401610701565b5050610bcb565b8360ff16600103610aa6579193508391336001600160a01b0384161480610a6d57506001600160a01b03851660009081526008602090815260408083208d845290915290205415155b610aa15760405162461bcd60e51b8152602060048201526005602482015264475330323560d81b6044820152606401610701565b610bcb565b601e8460ff161115610b6b576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018b9052600190605c0160405160208183030381529060405280519060200120600486610b0b9190612e09565b6040805160008152602081018083529390935260ff90911690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610b5a573d6000803e3d6000fd5b505050602060405103519450610bcb565b6040805160008152602081018083528c905260ff861691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa158015610bbe573d6000803e3d6000fd5b5050506020604051035194505b856001600160a01b0316856001600160a01b0316118015610c0557506001600160a01b038581166000908152600260205260409020541615155b8015610c1b57506001600160a01b038516600114155b610c4f5760405162461bcd60e51b815260206004820152600560248201526423a998191b60d91b6044820152606401610701565b9394508493600101610853565b50505050505050505050565b600060016001600160a01b03831614801590610c9d57506001600160a01b038281166000908152600160205260409020541615155b92915050565b60006001600160a01b038216600114801590610c9d5750506001600160a01b0390811660009081526002602052604090205416151590565b60007fb648d3644f584ed1c2232d53c46d87e693586486ad0d1175f8656013110b714e3386868686604051610d14959493929190612e5a565b60405180910390a1610d2885858585611c57565b95945050505050565b60006060610d4186868686610cdb565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b60606000610d76836020612ea6565b6001600160401b03811115610d8d57610d8d6125ad565b6040519080825280601f01601f191660200182016040528015610db7576020820181803683370190505b50905060005b83811015610dda5784810154602080830284010152600101610dbd565b509392505050565b610dea611bc6565b6001600160a01b03811615801590610e0c57506001600160a01b038116600114155b610e405760405162461bcd60e51b8152602060048201526005602482015264475331303160d81b6044820152606401610701565b6001600160a01b038181166000908152600160205260409020541615610e905760405162461bcd60e51b815260206004820152600560248201526423a998981960d91b6044820152606401610701565b600160208181527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f80546001600160a01b03858116600081815260408082208054949095166001600160a01b031994851617909455959095528254168417909155519182527fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844091015b60405180910390a150565b610f2c611bc6565b600354811115610f4e5760405162461bcd60e51b815260040161070190612ebd565b6001811015610f875760405162461bcd60e51b815260206004820152600560248201526423a999181960d91b6044820152606401610701565b60048190556040518181527f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c9390602001610f19565b600554600454604080516020810193909352339083015260608281019190915260009160800160405160208183030381529060405290507f66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed8d8d8d8d8d8d8d8d8d8d8d8c6040516110389c9b9a99989796959493929190612f05565b60405180910390a16110538d8d8d8d8d8d8d8d8d8d8d611d2e565b9d9c50505050505050505050505050565b6004548061109c5760405162461bcd60e51b8152602060048201526005602482015264475330303160d81b6044820152606401610701565b6110a884848484610806565b50505050565b606060006003546001600160401b038111156110cc576110cc6125ad565b6040519080825280602002602001820160405280156110f5578160200160208202803683370190505b506001600090815260026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e054919250906001600160a01b03165b6001600160a01b038116600114611196578083838151811061115657611156612f9c565b6001600160a01b0392831660209182029290920181019190915291811660009081526002909252604090912054168161118e81612da1565b925050611132565b509092915050565b600080825160208401855af480600052503d6020523d600060403e60403d016000fd5b6111ff8a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612077915050565b6001600160a01b0384161561123657611236847f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d555565b6112768787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061225392505050565b811561128d5761128b8260006001868561234d565b505b336001600160a01b03167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b896040516112ce959493929190612fb2565b60405180910390a250505050505050505050565b6000805a905061132b878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525089925050505a612453565b61133457600080fd5b60005a611341908361301e565b90508060405160200161135691815260200190565b60408051601f198184030181529082905262461bcd60e51b8252610701916004016127db565b60606000826001600160401b03811115611398576113986125ad565b6040519080825280602002602001820160405280156113c1578160200160208202803683370190505b506001600160a01b0380861660009081526001602052604081205492945091165b6001600160a01b0381161580159061140457506001600160a01b038116600114155b801561140f57508482105b15611467578084838151811061142757611427612f9c565b6001600160a01b0392831660209182029290920181019190915291811660009081526001909252604090912054168161145f81612da1565b9250506113e2565b908352919491935090915050565b336000908152600260205260409020546001600160a01b03166114c25760405162461bcd60e51b8152602060048201526005602482015264047533033360dc1b6044820152606401610701565b336000818152600860209081526040808320858452909152808220600190555183917ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c91a350565b600061151f8c8c8c8c8c8c8c8c8c8c8c6118ba565b8051906020012090509b9a5050505050505050505050565b61153f611bc6565b6001600160a01b0381161580159061156157506001600160a01b038116600114155b6115955760405162461bcd60e51b8152602060048201526005602482015264475331303160d81b6044820152606401610701565b6001600160a01b038281166000908152600160205260409020548116908216146115e95760405162461bcd60e51b8152602060048201526005602482015264475331303360d81b6044820152606401610701565b6001600160a01b038181166000818152600160209081526040808320805488871685528285208054919097166001600160a01b03199182161790965592849052825490941690915591519081527faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427691015b60405180910390a15050565b61166e611bc6565b7f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c88181556040516001600160a01b03831681527f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa29060200161165a565b6116d3611bc6565b6001600160a01b038116158015906116f557506001600160a01b038116600114155b801561170a57506001600160a01b0381163014155b6117265760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b03818116600090815260026020526040902054161561175e5760405162461bcd60e51b815260040161070190612d6c565b6001600160a01b0382161580159061178057506001600160a01b038216600114155b61179c5760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b038381166000908152600260205260409020548116908316146117f05760405162461bcd60e51b8152602060048201526005602482015264475332303560d81b6044820152606401610701565b6001600160a01b038281166000818152600260209081526040808320805487871680865283862080549289166001600160a01b0319938416179055968a1685528285208054821690971790965592849052825490941690915591519081527ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf910160405180910390a16040516001600160a01b03821681527f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea269060200160405180910390a1505050565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d6040516118f4929190613031565b60405190819003812061191a949392918e908e908e908e908e908e908e90602001613041565b60408051601f1981840301815291905280516020909101209050601960f81b600160f81b6119466119fc565b6040516001600160f81b031993841660208201529290911660218301526022820152604281018290526062016040516020818303038152906040529150509b9a5050505050505050505050565b61199b611bc6565b6119c3817f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d555565b6040516001600160a01b03821681527f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b090602001610f19565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692184660408051602081019390935282015230606082015260800160405160208183030381529060405280519060200120905090565b611a5b611bc6565b806001600354611a6b919061301e565b1015611a895760405162461bcd60e51b815260040161070190612ebd565b6001600160a01b03821615801590611aab57506001600160a01b038216600114155b611ac75760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b03838116600090815260026020526040902054811690831614611b1b5760405162461bcd60e51b8152602060048201526005602482015264475332303560d81b6044820152606401610701565b6001600160a01b03828116600081815260026020526040808220805488861684529183208054929095166001600160a01b03199283161790945591815282549091169091556003805491611b6e836130b0565b90915550506040516001600160a01b03831681527ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf9060200160405180910390a18060045414611bc157611bc181610f24565b505050565b333014611bfd5760405162461bcd60e51b8152602060048201526005602482015264475330333160d81b6044820152606401610701565b565b600082600003611c1157506000610c9d565b6000611c1d8385612ea6565b905082611c2a85836130c7565b14611c3457600080fd5b9392505050565b600080611c4883856130e9565b905083811015611c3457600080fd5b600033600114801590611c815750336000908152600160205260409020546001600160a01b031615155b611cb55760405162461bcd60e51b815260206004820152600560248201526411d4cc4c0d60da1b6044820152606401610701565b611cc2858585855a612453565b90508015611cfa5760405133907f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb890600090a2611d26565b60405133907facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37590600090a25b949350505050565b6000806000611d488e8e8e8e8e8e8e8e8e8e6005546118ba565b600580549192506000611d5a83612da1565b9091555050805160208201209150611d73828286611064565b506000611d9e7f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c85490565b90506001600160a01b03811615611e2457806001600160a01b03166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401611df19c9b9a999897969594939291906130fc565b600060405180830381600087803b158015611e0b57600080fd5b505af1158015611e1f573d6000803e3d6000fd5b505050505b611e50611e338a6109c46130e9565b603f611e408c6040612ea6565b611e4a91906130c7565b90612498565b611e5c906101f46130e9565b5a1015611e935760405162461bcd60e51b8152602060048201526005602482015264047533031360dc1b6044820152606401610701565b60005a9050611f048f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e8c600014611ef1578e612453565b6109c45a611eff919061301e565b612453565b9350611f115a82906124af565b90508380611f1e57508915155b80611f2857508715155b611f5c5760405162461bcd60e51b8152602060048201526005602482015264475330313360d81b6044820152606401610701565b60008815611f7457611f71828b8b8b8b61234d565b90505b8415611fb85760408051858152602081018390527f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e910160405180910390a1611ff2565b60408051858152602081018390527f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d23910160405180910390a15b50506001600160a01b0381161561206657604051631264e26d60e31b81526004810183905283151560248201526001600160a01b03821690639327136890604401600060405180830381600087803b15801561204d57600080fd5b505af1158015612061573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b600454156120af5760405162461bcd60e51b8152602060048201526005602482015264047533230360dc1b6044820152606401610701565b81518111156120d05760405162461bcd60e51b815260040161070190612ebd565b60018110156121095760405162461bcd60e51b815260206004820152600560248201526423a999181960d91b6044820152606401610701565b600160005b835181101561222057600084828151811061212b5761212b612f9c565b6020026020010151905060006001600160a01b0316816001600160a01b03161415801561216257506001600160a01b038116600114155b801561217757506001600160a01b0381163014155b80156121955750806001600160a01b0316836001600160a01b031614155b6121b15760405162461bcd60e51b815260040161070190612d4d565b6001600160a01b0381811660009081526002602052604090205416156121e95760405162461bcd60e51b815260040161070190612d6c565b6001600160a01b03928316600090815260026020526040902080546001600160a01b0319169382169390931790925560010161210e565b506001600160a01b0316600090815260026020526040902080546001600160a01b03191660011790559051600355600455565b600160008190526020527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f546001600160a01b0316156122bd5760405162461bcd60e51b8152602060048201526005602482015264047533130360dc1b6044820152606401610701565b6001600081905260208190527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f80546001600160a01b03191690911790556001600160a01b03821615610802576123198260008360015a612453565b6108025760405162461bcd60e51b8152602060048201526005602482015264047533030360dc1b6044820152606401610701565b6000806001600160a01b038316156123655782612367565b325b90506001600160a01b0384166123fa576123993a8610612387573a612389565b855b6123938989611c3b565b90611bff565b6040519092506001600160a01b0382169083156108fc029084906000818181858888f193505050506123f55760405162461bcd60e51b8152602060048201526005602482015264475330313160d81b6044820152606401610701565b612449565b612408856123938989611c3b565b91506124158482846124ca565b6124495760405162461bcd60e51b815260206004820152600560248201526423a998189960d91b6044820152606401610701565b5095945050505050565b6000600183600181111561246957612469612e22565b03612481576000808551602087018986f49050610d28565b600080855160208701888a87f19695505050505050565b6000818310156124a85781611c34565b5090919050565b6000828211156124be57600080fd5b6000611d26838561301e565b604080516001600160a01b03841660248201526044808201849052825180830390910181526064909101909152602080820180516001600160e01b031663a9059cbb60e01b1781528251600093929184919082896127105a03f13d801561253c5760208114612544576000935061254f565b81935061254f565b600051158215171593505b5050509392505050565b6001600160a01b038116811461256e57600080fd5b50565b803561257c81612559565b919050565b6000806040838503121561259457600080fd5b823561259f81612559565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126125d457600080fd5b81356001600160401b03808211156125ee576125ee6125ad565b604051601f8301601f19908116603f01168101908282118183101715612616576126166125ad565b8160405283815286602085880101111561262f57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561266557600080fd5b8435935060208501356001600160401b038082111561268357600080fd5b61268f888389016125c3565b945060408701359150808211156126a557600080fd5b506126b2878288016125c3565b949793965093946060013593505050565b6000602082840312156126d557600080fd5b8135611c3481612559565b80356002811061257c57600080fd5b6000806000806080858703121561270557600080fd5b843561271081612559565b93506020850135925060408501356001600160401b0381111561273257600080fd5b61273e878288016125c3565b92505061274d606086016126e0565b905092959194509250565b6000815180845260005b8181101561277e57602081850181015186830182015201612762565b506000602082860101526020601f19601f83011685010191505092915050565b8215158152604060208201526000611d266040830184612758565b600080604083850312156127cc57600080fd5b50508035926020909101359150565b602081526000611c346020830184612758565b60006020828403121561280057600080fd5b5035919050565b60008083601f84011261281957600080fd5b5081356001600160401b0381111561283057600080fd5b60208301915083602082850101111561284857600080fd5b9250929050565b60008060008060008060008060008060006101408c8e03121561287157600080fd5b61287a8c612571565b9a5060208c013599506001600160401b038060408e0135111561289c57600080fd5b6128ac8e60408f01358f01612807565b909a5098506128bd60608e016126e0565b975060808d0135965060a08d0135955060c08d013594506128e060e08e01612571565b93506128ef6101008e01612571565b9250806101208e0135111561290357600080fd5b506129158d6101208e01358e016125c3565b90509295989b509295989b9093969950565b60008060006060848603121561293c57600080fd5b8335925060208401356001600160401b038082111561295a57600080fd5b612966878388016125c3565b9350604086013591508082111561297c57600080fd5b50612989868287016125c3565b9150509250925092565b60008151808452602080850194506020840160005b838110156129cd5781516001600160a01b0316875295820195908201906001016129a8565b509495945050505050565b602081526000611c346020830184612993565b600080604083850312156129fe57600080fd5b8235612a0981612559565b915060208301356001600160401b03811115612a2457600080fd5b612a30858286016125c3565b9150509250929050565b6000806000806000806000806000806101008b8d031215612a5a57600080fd5b8a356001600160401b0380821115612a7157600080fd5b818d0191508d601f830112612a8557600080fd5b813581811115612a9457600080fd5b8e60208260051b8501011115612aa957600080fd5b60208381019d50909b508d01359950612ac460408e01612571565b985060608d0135915080821115612ada57600080fd5b50612ae78d828e01612807565b9097509550612afa905060808c01612571565b9350612b0860a08c01612571565b925060c08b01359150612b1d60e08c01612571565b90509295989b9194979a5092959850565b600080600080600060808688031215612b4657600080fd5b8535612b5181612559565b94506020860135935060408601356001600160401b03811115612b7357600080fd5b612b7f88828901612807565b9094509250612b929050606087016126e0565b90509295509295909350565b604081526000612bb16040830185612993565b905060018060a01b03831660208301529392505050565b60008060008060008060008060008060006101408c8e031215612bea57600080fd5b8b35612bf581612559565b9a5060208c0135995060408c01356001600160401b03811115612c1757600080fd5b612c238e828f01612807565b909a509850612c36905060608d016126e0565b965060808c0135955060a08c0135945060c08c0135935060e08c0135612c5b81612559565b92506101008c0135612c6c81612559565b809250506101208c013590509295989b509295989b9093969950565b60008060408385031215612c9b57600080fd5b8235612ca681612559565b91506020830135612cb681612559565b809150509250929050565b600080600060608486031215612cd657600080fd5b8335612ce181612559565b92506020840135612cf181612559565b91506040840135612d0181612559565b809150509250925092565b600080600060608486031215612d2157600080fd5b8335612d2c81612559565b92506020840135612d3c81612559565b929592945050506040919091013590565b602080825260059082015264475332303360d81b604082015260600190565b60208082526005908201526411d4cc8c0d60da1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201612db357612db3612d8b565b5060010190565b604081526000612dcd6040830185612758565b8281036020840152610d288185612758565b600060208284031215612df157600080fd5b81516001600160e01b031981168114611c3457600080fd5b60ff8281168282160390811115610c9d57610c9d612d8b565b634e487b7160e01b600052602160045260246000fd5b60028110612e5657634e487b7160e01b600052602160045260246000fd5b9052565b6001600160a01b038681168252851660208201526040810184905260a060608201819052600090612e8d90830185612758565b9050612e9c6080830184612e38565b9695505050505050565b8082028115828204841417610c9d57610c9d612d8b565b602080825260059082015264475332303160d81b604082015260600190565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600060018060a01b03808f1683528d60208401526101606040840152612f3061016084018d8f612edc565b612f3d606085018d612e38565b8a60808501528960a08501528860c085015281881660e0850152818716610100850152838103610120850152612f738187612758565b915050828103610140840152612f898185612758565b9f9e505050505050505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6080808252810185905260008660a08301825b88811015612ff5578235612fd881612559565b6001600160a01b0316825260209283019290910190600101612fc5565b50602084019690965250506001600160a01b039283166040820152911660609091015292915050565b81810381811115610c9d57610c9d612d8b565b8183823760009101908152919050565b8b81526001600160a01b038b81166020830152604082018b9052606082018a9052610160820190613075608084018b612e38565b60a083019890985260c082019690965260e0810194909452918516610100840152909316610120820152610140019190915295945050505050565b6000816130bf576130bf612d8b565b506000190190565b6000826130e457634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610c9d57610c9d612d8b565b600060018060a01b03808f1683528d6020840152610160604084015261312761016084018d8f612edc565b613134606085018d612e38565b8a60808501528960a08501528860c085015281881660e085015281871661010085015283810361012085015261316a8187612758565b925050808416610140840152509d9c5050505050505050505050505056fea2646970667358221220976dca493dd95f9b904ec290061e789606e429364fc397e8d8017c4baf11a91664736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/GnosisSafeProxyFactory.json b/apps/api/src/app/hardhat/export/GnosisSafeProxyFactory.json new file mode 100644 index 000000000..c3a58e082 --- /dev/null +++ b/apps/api/src/app/hardhat/export/GnosisSafeProxyFactory.json @@ -0,0 +1,172 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "GnosisSafeProxyFactory", + "sourceName": "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract GnosisSafeProxy", + "name": "proxy", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "singleton", + "type": "address" + } + ], + "name": "ProxyCreation", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_singleton", + "type": "address" + }, + { + "internalType": "bytes", + "name": "initializer", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + } + ], + "name": "calculateCreateProxyWithNonceAddress", + "outputs": [ + { + "internalType": "contract GnosisSafeProxy", + "name": "proxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "singleton", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createProxy", + "outputs": [ + { + "internalType": "contract GnosisSafeProxy", + "name": "proxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_singleton", + "type": "address" + }, + { + "internalType": "bytes", + "name": "initializer", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "contract IProxyCreationCallback", + "name": "callback", + "type": "address" + } + ], + "name": "createProxyWithCallback", + "outputs": [ + { + "internalType": "contract GnosisSafeProxy", + "name": "proxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_singleton", + "type": "address" + }, + { + "internalType": "bytes", + "name": "initializer", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + } + ], + "name": "createProxyWithNonce", + "outputs": [ + { + "internalType": "contract GnosisSafeProxy", + "name": "proxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proxyCreationCode", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "proxyRuntimeCode", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610a07806100206000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80631688f0b9146100675780632500510e1461009757806353e5d935146100aa57806361b69abd146100bf578063addacc0f146100d2578063d18af54d146100da575b600080fd5b61007a61007536600461057b565b6100ed565b6040516001600160a01b0390911681526020015b60405180910390f35b61007a6100a53660046105d4565b610168565b6100b26101fd565b60405161008e91906106af565b61007a6100cd3660046106c9565b610227565b6100b26102d0565b61007a6100e8366004610719565b6102e2565b60006100fa8484846103b8565b83519091501561011e5760008060008551602087016000865af10361011e57600080fd5b604080516001600160a01b038084168252861660208201527f4f51faf6c4561ff95f067657e43439f0f856d97c04d9ec9070a6199ad418e235910160405180910390a19392505050565b60006101ac8585858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508792506103b8915050565b6040516bffffffffffffffffffffffff19606083901b16602082015290915060340160408051601f198184030181529082905262461bcd60e51b82526101f4916004016106af565b60405180910390fd5b60606040518060200161020f906104a7565b601f1982820381018352601f90910116604052919050565b600082604051610236906104a7565b6001600160a01b039091168152602001604051809103906000f080158015610262573d6000803e3d6000fd5b508251909150156102875760008060008451602086016000865af10361028757600080fd5b604080516001600160a01b038084168252851660208201527f4f51faf6c4561ff95f067657e43439f0f856d97c04d9ec9070a6199ad418e235910160405180910390a192915050565b60606040518060200161020f906104b4565b600080838360405160200161031392919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b6040516020818303038152906040528051906020012060001c90506103398686836100ed565b91506001600160a01b038316156103af576040516303ca56a360e31b81526001600160a01b03841690631e52b5189061037c9085908a908a908a90600401610785565b600060405180830381600087803b15801561039657600080fd5b505af11580156103aa573d6000803e3d6000fd5b505050505b50949350505050565b6000808380519060200120836040516020016103de929190918252602082015260400190565b604051602081830303815290604052805190602001209050600060405180602001610408906104a7565b601f1982820381018352601f90910116604081905261043591906001600160a01b038916906020016107c2565b6040516020818303038152906040529050818151826020016000f592506001600160a01b03831661049e5760405162461bcd60e51b815260206004820152601360248201527210dc99585d194c8818d85b1b0819985a5b1959606a1b60448201526064016101f4565b50509392505050565b610172806107e583390190565b607b8061095783390190565b6001600160a01b03811681146104d557600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126104ff57600080fd5b813567ffffffffffffffff8082111561051a5761051a6104d8565b604051601f8301601f19908116603f01168101908282118183101715610542576105426104d8565b8160405283815286602085880101111561055b57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561059057600080fd5b833561059b816104c0565b9250602084013567ffffffffffffffff8111156105b757600080fd5b6105c3868287016104ee565b925050604084013590509250925092565b600080600080606085870312156105ea57600080fd5b84356105f5816104c0565b9350602085013567ffffffffffffffff8082111561061257600080fd5b818701915087601f83011261062657600080fd5b81358181111561063557600080fd5b88602082850101111561064757600080fd5b95986020929092019750949560400135945092505050565b60005b8381101561067a578181015183820152602001610662565b50506000910152565b6000815180845261069b81602086016020860161065f565b601f01601f19169290920160200192915050565b6020815260006106c26020830184610683565b9392505050565b600080604083850312156106dc57600080fd5b82356106e7816104c0565b9150602083013567ffffffffffffffff81111561070357600080fd5b61070f858286016104ee565b9150509250929050565b6000806000806080858703121561072f57600080fd5b843561073a816104c0565b9350602085013567ffffffffffffffff81111561075657600080fd5b610762878288016104ee565b93505060408501359150606085013561077a816104c0565b939692955090935050565b6001600160a01b038581168252841660208201526080604082018190526000906107b190830185610683565b905082606083015295945050505050565b600083516107d481846020880161065f565b919091019182525060200191905056fe608060405234801561001057600080fd5b5060405161017238038061017283398101604081905261002f916100b9565b6001600160a01b0381166100945760405162461bcd60e51b815260206004820152602260248201527f496e76616c69642073696e676c65746f6e20616464726573732070726f766964604482015261195960f21b606482015260840160405180910390fd5b600080546001600160a01b0319166001600160a01b03929092169190911790556100e9565b6000602082840312156100cb57600080fd5b81516001600160a01b03811681146100e257600080fd5b9392505050565b607b806100f76000396000f3fe6080604052600080546001600160a01b0316632cf35bc960e11b823501602757808252602082f35b3682833781823684845af490503d82833e806040573d82fd5b503d81f3fea26469706673582212205dab0f9c57a2510fe28d054b166e432aaa601af179ed1254de2ac839212d9a3c64736f6c634300081800336080604052600080546001600160a01b0316632cf35bc960e11b823501602757808252602082f35b3682833781823684845af490503d82833e806040573d82fd5b503d81f3fea26469706673582212205dab0f9c57a2510fe28d054b166e432aaa601af179ed1254de2ac839212d9a3c64736f6c63430008180033a2646970667358221220215f3b29e9636281b241e36d8ba0bc701de95e768f9c59b8aebdc67eb6c0cdfa64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100625760003560e01c80631688f0b9146100675780632500510e1461009757806353e5d935146100aa57806361b69abd146100bf578063addacc0f146100d2578063d18af54d146100da575b600080fd5b61007a61007536600461057b565b6100ed565b6040516001600160a01b0390911681526020015b60405180910390f35b61007a6100a53660046105d4565b610168565b6100b26101fd565b60405161008e91906106af565b61007a6100cd3660046106c9565b610227565b6100b26102d0565b61007a6100e8366004610719565b6102e2565b60006100fa8484846103b8565b83519091501561011e5760008060008551602087016000865af10361011e57600080fd5b604080516001600160a01b038084168252861660208201527f4f51faf6c4561ff95f067657e43439f0f856d97c04d9ec9070a6199ad418e235910160405180910390a19392505050565b60006101ac8585858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508792506103b8915050565b6040516bffffffffffffffffffffffff19606083901b16602082015290915060340160408051601f198184030181529082905262461bcd60e51b82526101f4916004016106af565b60405180910390fd5b60606040518060200161020f906104a7565b601f1982820381018352601f90910116604052919050565b600082604051610236906104a7565b6001600160a01b039091168152602001604051809103906000f080158015610262573d6000803e3d6000fd5b508251909150156102875760008060008451602086016000865af10361028757600080fd5b604080516001600160a01b038084168252851660208201527f4f51faf6c4561ff95f067657e43439f0f856d97c04d9ec9070a6199ad418e235910160405180910390a192915050565b60606040518060200161020f906104b4565b600080838360405160200161031392919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b6040516020818303038152906040528051906020012060001c90506103398686836100ed565b91506001600160a01b038316156103af576040516303ca56a360e31b81526001600160a01b03841690631e52b5189061037c9085908a908a908a90600401610785565b600060405180830381600087803b15801561039657600080fd5b505af11580156103aa573d6000803e3d6000fd5b505050505b50949350505050565b6000808380519060200120836040516020016103de929190918252602082015260400190565b604051602081830303815290604052805190602001209050600060405180602001610408906104a7565b601f1982820381018352601f90910116604081905261043591906001600160a01b038916906020016107c2565b6040516020818303038152906040529050818151826020016000f592506001600160a01b03831661049e5760405162461bcd60e51b815260206004820152601360248201527210dc99585d194c8818d85b1b0819985a5b1959606a1b60448201526064016101f4565b50509392505050565b610172806107e583390190565b607b8061095783390190565b6001600160a01b03811681146104d557600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126104ff57600080fd5b813567ffffffffffffffff8082111561051a5761051a6104d8565b604051601f8301601f19908116603f01168101908282118183101715610542576105426104d8565b8160405283815286602085880101111561055b57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561059057600080fd5b833561059b816104c0565b9250602084013567ffffffffffffffff8111156105b757600080fd5b6105c3868287016104ee565b925050604084013590509250925092565b600080600080606085870312156105ea57600080fd5b84356105f5816104c0565b9350602085013567ffffffffffffffff8082111561061257600080fd5b818701915087601f83011261062657600080fd5b81358181111561063557600080fd5b88602082850101111561064757600080fd5b95986020929092019750949560400135945092505050565b60005b8381101561067a578181015183820152602001610662565b50506000910152565b6000815180845261069b81602086016020860161065f565b601f01601f19169290920160200192915050565b6020815260006106c26020830184610683565b9392505050565b600080604083850312156106dc57600080fd5b82356106e7816104c0565b9150602083013567ffffffffffffffff81111561070357600080fd5b61070f858286016104ee565b9150509250929050565b6000806000806080858703121561072f57600080fd5b843561073a816104c0565b9350602085013567ffffffffffffffff81111561075657600080fd5b610762878288016104ee565b93505060408501359150606085013561077a816104c0565b939692955090935050565b6001600160a01b038581168252841660208201526080604082018190526000906107b190830185610683565b905082606083015295945050505050565b600083516107d481846020880161065f565b919091019182525060200191905056fe608060405234801561001057600080fd5b5060405161017238038061017283398101604081905261002f916100b9565b6001600160a01b0381166100945760405162461bcd60e51b815260206004820152602260248201527f496e76616c69642073696e676c65746f6e20616464726573732070726f766964604482015261195960f21b606482015260840160405180910390fd5b600080546001600160a01b0319166001600160a01b03929092169190911790556100e9565b6000602082840312156100cb57600080fd5b81516001600160a01b03811681146100e257600080fd5b9392505050565b607b806100f76000396000f3fe6080604052600080546001600160a01b0316632cf35bc960e11b823501602757808252602082f35b3682833781823684845af490503d82833e806040573d82fd5b503d81f3fea26469706673582212205dab0f9c57a2510fe28d054b166e432aaa601af179ed1254de2ac839212d9a3c64736f6c634300081800336080604052600080546001600160a01b0316632cf35bc960e11b823501602757808252602082f35b3682833781823684845af490503d82833e806040573d82fd5b503d81f3fea26469706673582212205dab0f9c57a2510fe28d054b166e432aaa601af179ed1254de2ac839212d9a3c64736f6c63430008180033a2646970667358221220215f3b29e9636281b241e36d8ba0bc701de95e768f9c59b8aebdc67eb6c0cdfa64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/Launchpad.json b/apps/api/src/app/hardhat/export/Launchpad.json new file mode 100644 index 000000000..ad0bb3f89 --- /dev/null +++ b/apps/api/src/app/hardhat/export/Launchpad.json @@ -0,0 +1,183 @@ +{ + "_format": "hh-vyper-artifact-1", + "contractName": "Launchpad", + "sourceName": "contracts/Launchpad.vy", + "abi": [ + { + "name": "VESystemCreated", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": true + }, + { + "name": "votingEscrow", + "type": "address", + "indexed": false + }, + { + "name": "rewardDistributor", + "type": "address", + "indexed": false + }, + { + "name": "rewardFaucet", + "type": "address", + "indexed": false + }, + { + "name": "admin", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "constructor", + "inputs": [ + { + "name": "_votingEscrow", + "type": "address" + }, + { + "name": "_rewardDistributor", + "type": "address" + }, + { + "name": "_rewardFaucet", + "type": "address" + }, + { + "name": "_balToken", + "type": "address" + }, + { + "name": "_balMinter", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deploy", + "inputs": [ + { + "name": "tokenBptAddr", + "type": "address" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "maxLockTime", + "type": "uint256" + }, + { + "name": "rewardDistributorStartTime", + "type": "uint256" + }, + { + "name": "admin_unlock_all", + "type": "address" + }, + { + "name": "admin_early_unlock", + "type": "address" + }, + { + "name": "rewardReceiver", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "votingEscrow", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "rewardDistributor", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "rewardFaucet", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balToken", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balMinter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + } + ], + "bytecode": "0x60206106c76000396000518060a01c6106c25760405260206106e76000396000518060a01c6106c25760605260206107076000396000518060a01c6106c25760805260206107276000396000518060a01c6106c25760a05260206107476000396000518060a01c6106c25760c052346106c257604051156100b157606051156100aa57608051156100a35760a0511561009c5760c05115156100b4565b60006100b4565b60006100b4565b60006100b4565b60005b61011757600c60e0527f7a65726f206164647265737300000000000000000000000000000000000000006101005260e05060e0518061010001601f826000031636823750506308c379a060a052602060c052601f19601f60e051011660440160bcfd5b60405161057552606051610595526080516105b55260a0516105d55260c0516105f55261057561014c61000039610615610000f36003361161000c5761055d565b60003560e01c346105635763915c40938118610498576101443610610563576004358060a01c6105635760405260243560040160408135116105635780358060605260208201818160803750505060443560040160208135116105635780358060c05260208201803560e05250505060a4358060a01c610563576101005260c4358060a01c610563576101205260e4358060a01c610563576101405260405160206105d560003960005118610121576004610160527f2162616c000000000000000000000000000000000000000000000000000000006101805261016050610160518061018001601f826000031636823750506308c379a061012052602061014052601f19601f61016051011660440161013cfd5b7f602d3d8160093d39f3363d3d373d3d3d363d730000000000000000000000000061018052602061057560003960005160601b610193527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006101a75260366101806000f0801561056357610160527f602d3d8160093d39f3363d3d373d3d3d363d73000000000000000000000000006101a052602061059560003960005160601b6101b3527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006101c75260366101a06000f08015610563576101805260016101a052610140516101c0526101405161022257610180516101c05260006101a0525b61016051639bf6abf36101e052610180604051610200528061022052806102000160605180825260208201818183608060045afa5050508051806020830101601f82600003163682375050601f19601f825160200101169050810190508061024052806102000160c0518082526020820160e051815250508051806020830101601f82600003163682375050601f19601f8251602001011690508101905033610260526101005161028052610120516102a0526064356102c05260206105d56000396000516102e05260206105f5600039600051610300526101c051610320526101a05161034052610180516103605250803b156105635760006101e06102246101fc6000855af1610339573d600060003e3d6000fd5b507f602d3d8160093d39f3363d3d373d3d3d363d73000000000000000000000000006102005260206105b560003960005160601b610213527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006102275260366102006000f08015610563576101e0526101805163be2030946102005261016051610220526101e05161024052608435610260523361028052803b15610563576000610200608461021c6000855af16103f6573d600060003e3d6000fd5b506101e05163c4d66de8610200526101805161022052803b15610563576000610200602461021c6000855af1610431573d600060003e3d6000fd5b506040517fa1de2bb5131ee0bd88e8ab94ac9f5ecf9e9d0bcea61a294eb7df4bf7c222b641610160516102005261018051610220526101e0516102405233610260526080610200a2610160516102005261018051610220526101e051610240526060610200f35b634f2bfe5b81186104bf576004361061056357602061057560003960005160405260206040f35b63acc2166a81186104e6576004361061056357602061059560003960005160405260206040f35b63397bcd41811861050d57600436106105635760206105b560003960005160405260206040f35b6338d54645811861053457600436106105635760206105d560003960005160405260206040f35b6373f43d6d811861055b57600436106105635760206105f560003960005160405260206040f35b505b60006000fd5b600080fda165767970657283000307000b005b600080fd", + "deployedBytecode": "0x6003361161000c5761055d565b60003560e01c346105635763915c40938118610498576101443610610563576004358060a01c6105635760405260243560040160408135116105635780358060605260208201818160803750505060443560040160208135116105635780358060c05260208201803560e05250505060a4358060a01c610563576101005260c4358060a01c610563576101205260e4358060a01c610563576101405260405160206105d560003960005118610121576004610160527f2162616c000000000000000000000000000000000000000000000000000000006101805261016050610160518061018001601f826000031636823750506308c379a061012052602061014052601f19601f61016051011660440161013cfd5b7f602d3d8160093d39f3363d3d373d3d3d363d730000000000000000000000000061018052602061057560003960005160601b610193527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006101a75260366101806000f0801561056357610160527f602d3d8160093d39f3363d3d373d3d3d363d73000000000000000000000000006101a052602061059560003960005160601b6101b3527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006101c75260366101a06000f08015610563576101805260016101a052610140516101c0526101405161022257610180516101c05260006101a0525b61016051639bf6abf36101e052610180604051610200528061022052806102000160605180825260208201818183608060045afa5050508051806020830101601f82600003163682375050601f19601f825160200101169050810190508061024052806102000160c0518082526020820160e051815250508051806020830101601f82600003163682375050601f19601f8251602001011690508101905033610260526101005161028052610120516102a0526064356102c05260206105d56000396000516102e05260206105f5600039600051610300526101c051610320526101a05161034052610180516103605250803b156105635760006101e06102246101fc6000855af1610339573d600060003e3d6000fd5b507f602d3d8160093d39f3363d3d373d3d3d363d73000000000000000000000000006102005260206105b560003960005160601b610213527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006102275260366102006000f08015610563576101e0526101805163be2030946102005261016051610220526101e05161024052608435610260523361028052803b15610563576000610200608461021c6000855af16103f6573d600060003e3d6000fd5b506101e05163c4d66de8610200526101805161022052803b15610563576000610200602461021c6000855af1610431573d600060003e3d6000fd5b506040517fa1de2bb5131ee0bd88e8ab94ac9f5ecf9e9d0bcea61a294eb7df4bf7c222b641610160516102005261018051610220526101e0516102405233610260526080610200a2610160516102005261018051610220526101e051610240526060610200f35b634f2bfe5b81186104bf576004361061056357602061057560003960005160405260206040f35b63acc2166a81186104e6576004361061056357602061059560003960005160405260206040f35b63397bcd41811861050d57600436106105635760206105b560003960005160405260206040f35b6338d54645811861053457600436106105635760206105d560003960005160405260206040f35b6373f43d6d811861055b57600436106105635760206105f560003960005160405260206040f35b505b60006000fd5b600080fda165767970657283000307000b", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/LensReward.json b/apps/api/src/app/hardhat/export/LensReward.json new file mode 100644 index 000000000..ccec8ce42 --- /dev/null +++ b/apps/api/src/app/hardhat/export/LensReward.json @@ -0,0 +1,93 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "LensReward", + "sourceName": "contracts/LensReward.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "contract IRewardDistributor", + "name": "distributor", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getUserClaimableReward", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "claimableAmount", + "type": "uint256" + } + ], + "internalType": "struct LensReward.ClaimableRewards", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IRewardDistributor", + "name": "distributor", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "getUserClaimableRewardsAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "claimableAmount", + "type": "uint256" + } + ], + "internalType": "struct LensReward.ClaimableRewards[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610552806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635753bfca1461003b57806360491ff414610064575b600080fd5b61004e61004936600461030f565b610084565b60405161005b919061035a565b60405180910390f35b61007761007236600461037c565b610221565b60405161005b9190610411565b60408051808201909152600080825260208201526040516370a0823160e01b81526001600160a01b038481166004830152600091908416906370a0823190602401602060405180830381865afa1580156100e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101069190610471565b60405163ca31879d60e01b81526001600160a01b03868116600483015285811660248301529192509086169063ca31879d906044016020604051808303816000875af115801561015a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061017e9190610471565b506040516370a0823160e01b81526001600160a01b038581166004830152600091908516906370a0823190602401602060405180830381865afa1580156101c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ed9190610471565b90506040518060400160405280856001600160a01b03168152602001838361021591906104a0565b90529695505050505050565b60608160008167ffffffffffffffff81111561023f5761023f6104b3565b60405190808252806020026020018201604052801561028457816020015b604080518082019091526000808252602082015281526020019060019003908161025d5790505b50905060005b828110156102ec576102be88888888858181106102a9576102a96104c9565b905060200201602081019061004991906104df565b8282815181106102d0576102d06104c9565b6020026020010181905250806102e590610503565b905061028a565b509695505050505050565b6001600160a01b038116811461030c57600080fd5b50565b60008060006060848603121561032457600080fd5b833561032f816102f7565b9250602084013561033f816102f7565b9150604084013561034f816102f7565b809150509250925092565b81516001600160a01b0316815260208083015190820152604081015b92915050565b6000806000806060858703121561039257600080fd5b843561039d816102f7565b935060208501356103ad816102f7565b9250604085013567ffffffffffffffff808211156103ca57600080fd5b818701915087601f8301126103de57600080fd5b8135818111156103ed57600080fd5b8860208260051b850101111561040257600080fd5b95989497505060200194505050565b602080825282518282018190526000919060409081850190868401855b828110156104645761045484835180516001600160a01b03168252602090810151910152565b928401929085019060010161042e565b5091979650505050505050565b60006020828403121561048357600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156103765761037661048a565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000602082840312156104f157600080fd5b81356104fc816102f7565b9392505050565b6000600182016105155761051561048a565b506001019056fea264697066735822122027644c9f1cbb10d96d582d4eeb73f545853df009df64befe97d316d4df9f2cf264736f6c63430008120033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80635753bfca1461003b57806360491ff414610064575b600080fd5b61004e61004936600461030f565b610084565b60405161005b919061035a565b60405180910390f35b61007761007236600461037c565b610221565b60405161005b9190610411565b60408051808201909152600080825260208201526040516370a0823160e01b81526001600160a01b038481166004830152600091908416906370a0823190602401602060405180830381865afa1580156100e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101069190610471565b60405163ca31879d60e01b81526001600160a01b03868116600483015285811660248301529192509086169063ca31879d906044016020604051808303816000875af115801561015a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061017e9190610471565b506040516370a0823160e01b81526001600160a01b038581166004830152600091908516906370a0823190602401602060405180830381865afa1580156101c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ed9190610471565b90506040518060400160405280856001600160a01b03168152602001838361021591906104a0565b90529695505050505050565b60608160008167ffffffffffffffff81111561023f5761023f6104b3565b60405190808252806020026020018201604052801561028457816020015b604080518082019091526000808252602082015281526020019060019003908161025d5790505b50905060005b828110156102ec576102be88888888858181106102a9576102a96104c9565b905060200201602081019061004991906104df565b8282815181106102d0576102d06104c9565b6020026020010181905250806102e590610503565b905061028a565b509695505050505050565b6001600160a01b038116811461030c57600080fd5b50565b60008060006060848603121561032457600080fd5b833561032f816102f7565b9250602084013561033f816102f7565b9150604084013561034f816102f7565b809150509250925092565b81516001600160a01b0316815260208083015190820152604081015b92915050565b6000806000806060858703121561039257600080fd5b843561039d816102f7565b935060208501356103ad816102f7565b9250604085013567ffffffffffffffff808211156103ca57600080fd5b818701915087601f8301126103de57600080fd5b8135818111156103ed57600080fd5b8860208260051b850101111561040257600080fd5b95989497505060200194505050565b602080825282518282018190526000919060409081850190868401855b828110156104645761045484835180516001600160a01b03168252602090810151910152565b928401929085019060010161042e565b5091979650505050505050565b60006020828403121561048357600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156103765761037661048a565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000602082840312156104f157600080fd5b81356104fc816102f7565b9392505050565b6000600182016105155761051561048a565b506001019056fea264697066735822122027644c9f1cbb10d96d582d4eeb73f545853df009df64befe97d316d4df9f2cf264736f6c63430008120033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/LimitedSupplyToken.json b/apps/api/src/app/hardhat/export/LimitedSupplyToken.json new file mode 100644 index 000000000..18d4a2eb5 --- /dev/null +++ b/apps/api/src/app/hardhat/export/LimitedSupplyToken.json @@ -0,0 +1,298 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/apps/api/src/app/hardhat/export/MultiSend.json b/apps/api/src/app/hardhat/export/MultiSend.json new file mode 100644 index 000000000..a9d401966 --- /dev/null +++ b/apps/api/src/app/hardhat/export/MultiSend.json @@ -0,0 +1,29 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "MultiSend", + "sourceName": "@gnosis.pm/safe-contracts/contracts/libraries/MultiSend.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "transactions", + "type": "bytes" + } + ], + "name": "multiSend", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "bytecode": "0x60a060405234801561001057600080fd5b503060805260805161025261002f6000396000604201526102526000f3fe60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b61003661003136600461016b565b610038565b005b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036100cd5760405162461bcd60e51b815260206004820152603060248201527f4d756c746953656e642073686f756c64206f6e6c792062652063616c6c65642060448201526f1d9a584819195b1959d85d1958d85b1b60821b606482015260840160405180910390fd5b805160205b81811015610150578083015160f81c6001820184015160601c601583018501516035840186015160558501870160008560008114610117576001811461012757610132565b6000808585888a5af19150610132565b6000808585895af491505b508061013d57600080fd5b50508060550185019450505050506100d2565b505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561017d57600080fd5b813567ffffffffffffffff8082111561019557600080fd5b818401915084601f8301126101a957600080fd5b8135818111156101bb576101bb610155565b604051601f8201601f19908116603f011681019083821181831017156101e3576101e3610155565b816040528281528760208487010111156101fc57600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122048ffb0b3f2f2f62a39869c615643ea2c865185e980e34e3526010f09632332aa64736f6c63430008180033", + "deployedBytecode": "0x60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b61003661003136600461016b565b610038565b005b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036100cd5760405162461bcd60e51b815260206004820152603060248201527f4d756c746953656e642073686f756c64206f6e6c792062652063616c6c65642060448201526f1d9a584819195b1959d85d1958d85b1b60821b606482015260840160405180910390fd5b805160205b81811015610150578083015160f81c6001820184015160601c601583018501516035840186015160558501870160008560008114610117576001811461012757610132565b6000808585888a5af19150610132565b6000808585895af491505b508061013d57600080fd5b50508060550185019450505050506100d2565b505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561017d57600080fd5b813567ffffffffffffffff8082111561019557600080fd5b818401915084601f8301126101a957600080fd5b8135818111156101bb576101bb610155565b604051601f8201601f19908116603f011681019083821181831017156101e3576101e3610155565b816040528281528760208487010111156101fc57600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122048ffb0b3f2f2f62a39869c615643ea2c865185e980e34e3526010f09632332aa64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/MultiSendCallOnly.json b/apps/api/src/app/hardhat/export/MultiSendCallOnly.json new file mode 100644 index 000000000..0582dd99b --- /dev/null +++ b/apps/api/src/app/hardhat/export/MultiSendCallOnly.json @@ -0,0 +1,24 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "MultiSendCallOnly", + "sourceName": "@gnosis.pm/safe-contracts/contracts/libraries/MultiSendCallOnly.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "bytes", + "name": "transactions", + "type": "bytes" + } + ], + "name": "multiSend", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506101ae806100206000396000f3fe60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b6100366100313660046100c7565b610038565b005b805160205b818110156100ac578083015160f81c6001820184015160601c601583018501516035840186015160558501870160008560008114610082576001811461001e5761008e565b6000808585888a5af191505b508061009957600080fd5b505080605501850194505050505061003d565b505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156100d957600080fd5b813567ffffffffffffffff808211156100f157600080fd5b818401915084601f83011261010557600080fd5b813581811115610117576101176100b1565b604051601f8201601f19908116603f0116810190838211818310171561013f5761013f6100b1565b8160405282815287602084870101111561015857600080fd5b82602086016020830137600092810160200192909252509594505050505056fea26469706673582212207730848407a4c5a77116bb261887c8305f7996baf682400d9f5a0ab7cd4f3ce064736f6c63430008180033", + "deployedBytecode": "0x60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b6100366100313660046100c7565b610038565b005b805160205b818110156100ac578083015160f81c6001820184015160601c601583018501516035840186015160558501870160008560008114610082576001811461001e5761008e565b6000808585888a5af191505b508061009957600080fd5b505080605501850194505050505061003d565b505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156100d957600080fd5b813567ffffffffffffffff808211156100f157600080fd5b818401915084601f83011261010557600080fd5b813581811115610117576101176100b1565b604051601f8201601f19908116603f0116810190838211818310171561013f5761013f6100b1565b8160405282815287602084870101111561015857600080fd5b82602086016020830137600092810160200192909252509594505050505056fea26469706673582212207730848407a4c5a77116bb261887c8305f7996baf682400d9f5a0ab7cd4f3ce064736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/NonFungibleToken.json b/apps/api/src/app/hardhat/export/NonFungibleToken.json new file mode 100644 index 000000000..a9e8c1adb --- /dev/null +++ b/apps/api/src/app/hardhat/export/NonFungibleToken.json @@ -0,0 +1,744 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "string", + "name": "baseURI_", + "type": "string" + }, + { + "internalType": "address", + "name": "owner_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINTER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "string", + "name": "_tokenURI", + "type": "string" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/apps/api/src/app/hardhat/export/RewardDistributor.json b/apps/api/src/app/hardhat/export/RewardDistributor.json new file mode 100644 index 000000000..18ccb672a --- /dev/null +++ b/apps/api/src/app/hardhat/export/RewardDistributor.json @@ -0,0 +1,670 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "RewardDistributor", + "sourceName": "contracts/RewardDistributor.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "NewAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "OnlyCallerOptIn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "TokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastCheckpointTimestamp", + "type": "uint256" + } + ], + "name": "TokenCheckpointed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "userTokenTimeCursor", + "type": "uint256" + } + ], + "name": "TokensClaimed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "addAllowedRewardTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowedRewardTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkpoint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "checkpointToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "checkpointTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "checkpointUser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "claimToken", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "claimTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "depositToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "depositTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "faucetDepositToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllowedRewardTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getNextNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTimeCursor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenLastBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenTimeCursor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "getTokensDistributedInWeek", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "getTotalSupplyAtTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "getUserBalanceAtTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserTimeCursor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getUserTokenTimeCursor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVotingEscrow", + "outputs": [ + { + "internalType": "contract IVotingEscrow", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVotingEscrow", + "name": "votingEscrow", + "type": "address" + }, + { + "internalType": "contract IRewardFaucet", + "name": "rewardFaucet_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "isOnlyCallerEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rewardFaucet", + "outputs": [ + { + "internalType": "contract IRewardFaucet", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setOnlyCallerCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "setOnlyCallerCheckWithSignature", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60e060405234801561001057600080fd5b5060408051808201825260118152702932bbb0b9322234b9ba3934b13aba37b960791b602080830191825283518085019094526001808552603160f81b9185019182529251909120608052915190912060a0527f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60c05260025560805160a05160c051612b476100b660003980611d9e525080611de0525080611dbf5250612b476000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c806390193b7c1161010f578063de681faf116100a2578063ed3a088711610071578063ed3a0887146103ea578063f213bd8c146103fd578063f851a44014610410578063fcaa54ee14610418576101e5565b8063de681faf146103a7578063e811f44b146103ba578063ece7514d146103cd578063ed24911d146103e2576101e5565b8063be203094116100de578063be20309414610366578063c2c4c5c114610379578063ca31879d14610381578063d3dc4ca114610394576101e5565b806390193b7c1461031a578063905d10ac1461032d578063a1648aa514610340578063acbc142814610353576101e5565b80634c9a47d8116101875780638050a7ee116101565780638050a7ee146102cc57806382aa5ad4146102df578063876e69a1146102e757806388720467146102fa576101e5565b80634c9a47d8146102805780634f3c50901461029357806375829def146102a65780637b8d6221146102b9576101e5565b8063338b5dea116101c3578063338b5dea1461023d5780633902b9bc14610250578063392e53cd14610263578063397bcd4114610278576101e5565b806308b0308a146101ea57806314866e08146102085780632308805b1461021d575b600080fd5b6101f261042b565b6040516101ff91906127b8565b60405180910390f35b61021b61021636600461248e565b61043f565b005b61023061022b36600461248e565b61045b565b6040516101ff9190612892565b61021b61024b3660046125e0565b61048a565b61021b61025e36600461248e565b610543565b61026b61058e565b6040516101ff9190612887565b6101f2610597565b61021b61028e3660046125e0565b6105a6565b6102306102a1366004612788565b610633565b61021b6102b436600461248e565b610645565b61021b6102c736600461264a565b6106df565b6102306102da3660046125a8565b61087e565b610230610893565b6102306102f536600461248e565b610899565b61030d6103083660046124aa565b6108c4565b6040516101ff919061284f565b61023061032836600461248e565b610a87565b61021b61033b36600461260b565b610aa2565b61026b61034e36600461248e565b610b35565b61023061036136600461248e565b610b53565b61021b6103743660046126cc565b610b7e565b61021b610d45565b61023061038f3660046125a8565b610d5f565b6102306103a23660046125e0565b610e41565b6102306103b53660046125e0565b610e69565b61021b6103c83660046126b2565b610e91565b6103d5610e9b565b6040516101ff919061280e565b610230610efd565b61026b6103f836600461248e565b610f0c565b61021b61040b36600461260b565b610f21565b6101f26110bc565b61021b6104263660046124fc565b6110cb565b60035461010090046001600160a01b031690565b610447611159565b61045081611170565b610458611566565b50565b6001600160a01b0381166000908152600b6020526040902054600160801b90046001600160801b03165b919050565b610492611159565b6001600160a01b0382166000908152600a602052604090205460ff166104d35760405162461bcd60e51b81526004016104ca906128e3565b60405180910390fd5b6104de82600061156d565b6104f36001600160a01b0383163330846118e8565b6104fe82600161156d565b7f95bf5847357310d24f8d03d8bad76c8ee329dfd3a3cb200df21c7bd1619e93bd828260405161052f9291906127f5565b60405180910390a161053f611566565b5050565b61054b611159565b6001600160a01b0381166000908152600a602052604090205460ff166105835760405162461bcd60e51b81526004016104ca906128e3565b61045081600161156d565b60035460ff1681565b6004546001600160a01b031681565b6001600160a01b0382166000908152600a602052604090205460ff166105de5760405162461bcd60e51b81526004016104ca906128e3565b6004546001600160a01b031633146106085760405162461bcd60e51b81526004016104ca9061297b565b61061382600061156d565b6106286001600160a01b0383163330846118e8565b61053f82600161156d565b60009081526007602052604090205490565b6008546001600160a01b0316331461066f5760405162461bcd60e51b81526004016104ca90612ab6565b6001600160a01b0381166106955760405162461bcd60e51b81526004016104ca90612a1c565b600880546001600160a01b0319166001600160a01b0383169081179091556040517f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c90600090a250565b6106e7611159565b6106f18382611942565b8260005b8181101561086e57600a600087878481811061070d57fe5b9050602002016020810190610722919061248e565b6001600160a01b0316815260208101919091526040016000205460ff1661075b5760405162461bcd60e51b81526004016104ca906128e3565b61078686868381811061076a57fe5b905060200201602081019061077f919061248e565b600061156d565b6107d0333086868581811061079757fe5b905060200201358989868181106107aa57fe5b90506020020160208101906107bf919061248e565b6001600160a01b03169291906118e8565b6107fb8686838181106107df57fe5b90506020020160208101906107f4919061248e565b600161156d565b7f95bf5847357310d24f8d03d8bad76c8ee329dfd3a3cb200df21c7bd1619e93bd86868381811061082857fe5b905060200201602081019061083d919061248e565b85858481811061084957fe5b9050602002013560405161085e9291906127f5565b60405180910390a16001016106f5565b5050610878611566565b50505050565b600061088a838361194f565b90505b92915050565b60065490565b6001600160a01b03166000908152600d6020526040902054600160401b90046001600160401b031690565b60606108ce611159565b836108d8816119cc565b6108e0611a04565b6108e985611170565b826000816001600160401b038111801561090257600080fd5b5060405190808252806020026020018201604052801561092c578160200160208202803683370190505b50905060005b82811015610a7357600a600088888481811061094a57fe5b905060200201602081019061095f919061248e565b6001600160a01b0316815260208101919091526040016000205460ff166109985760405162461bcd60e51b81526004016104ca906128e3565b6109a787878381811061076a57fe5b6109d1888888848181106109b757fe5b90506020020160208101906109cc919061248e565b611b58565b8282815181106109dd57fe5b60209081029190910101526004546001600160a01b031663c7b56abe888884818110610a0557fe5b9050602002016020810190610a1a919061248e565b6040518263ffffffff1660e01b8152600401610a3691906127b8565b600060405180830381600087803b158015610a5057600080fd5b505af1158015610a64573d6000803e3d6000fd5b50505050806001019050610932565b5092505050610a80611566565b9392505050565b6001600160a01b031660009081526020819052604090205490565b610aaa611159565b8060005b81811015610b2b57600a6000858584818110610ac657fe5b9050602002016020810190610adb919061248e565b6001600160a01b0316815260208101919091526040016000205460ff16610b145760405162461bcd60e51b81526004016104ca906128e3565b610b238484838181106107df57fe5b600101610aae565b505061053f611566565b6001600160a01b031660009081526001602052604090205460ff1690565b6001600160a01b03166000908152600b6020526040902054600160401b90046001600160401b031690565b60035460ff1615610ba15760405162461bcd60e51b81526004016104ca9061293c565b6003805460ff191660011790556001600160a01b03811615801590610bce57506001600160a01b03831615155b610bea5760405162461bcd60e51b81526004016104ca9061295c565b600880546001600160a01b038084166001600160a01b0319928316179092556004805486841692169190911790556003805491861661010002610100600160a81b0319909216919091179055610c3f82611d2a565b91506000610c4c42611d2a565b905080831015610c6e5760405162461bcd60e51b81526004016104ca906129e7565b80625c490001831115610c935760405162461bcd60e51b81526004016104ca90612a42565b80831415610d375760405163bd85b03960e01b81526000906001600160a01b0387169063bd85b03990610cca908590600401612892565b60206040518083038186803b158015610ce257600080fd5b505afa158015610cf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1a91906127a0565b11610d375760405162461bcd60e51b81526004016104ca90612a6e565b505060058190556006555050565b610d4d611159565b610d55611a04565b610d5d611566565b565b6000610d69611159565b82610d73816119cc565b6001600160a01b0383166000908152600a602052604090205460ff16610dab5760405162461bcd60e51b81526004016104ca906128e3565b610db3611a04565b610dbc84611170565b610dc783600061156d565b6000610dd38585611b58565b600480546040516363dab55f60e11b81529293506001600160a01b03169163c7b56abe91610e03918891016127b8565b600060405180830381600087803b158015610e1d57600080fd5b505af1158015610e31573d6000803e3d6000fd5b509294505050505061088d611566565b6001600160a01b03919091166000908152600c60209081526040808320938352929052205490565b6001600160a01b03919091166000908152600e60209081526040808320938352929052205490565b6104583382611d36565b60606009805480602002602001604051908101604052809291908181526020018280548015610ef357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ed5575b5050505050905090565b6000610f07611d9a565b905090565b600a6020526000908152604090205460ff1681565b6008546001600160a01b03163314610f4b5760405162461bcd60e51b81526004016104ca90612ab6565b60005b818110156110b757600a6000848484818110610f6657fe5b9050602002016020810190610f7b919061248e565b6001600160a01b0316815260208101919091526040016000205460ff1615610fb55760405162461bcd60e51b81526004016104ca906128bc565b6001600a6000858585818110610fc757fe5b9050602002016020810190610fdc919061248e565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055600983838381811061101257fe5b9050602002016020810190611027919061248e565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905582828281811061106557fe5b905060200201602081019061107a919061248e565b6001600160a01b03167f784c8f4dbf0ffedd6e72c76501c545a70f8b203b30a26ce542bf92ba87c248a460405160405180910390a2600101610f4e565b505050565b6008546001600160a01b031681565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a9084846110f987610a87565b60405160200180858152602001846001600160a01b03168152602001831515815260200182815260200194505050505060405160208183030381529060405280519060200120905061114f8482846101f8611e58565b6108788484611d36565b61116a600280541415610190611e67565b60028055565b60035460405163010ae75760e01b815260009161010090046001600160a01b03169063010ae757906111a69085906004016127b8565b60206040518083038186803b1580156111be57600080fd5b505afa1580156111d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f691906127a0565b9050806112035750610458565b6001600160a01b0382166000908152600d6020526040812080549091600160401b9091046001600160401b0316908161124c5761124585600554600087611e75565b9050611289565b42821061125c5750505050610458565b508154600160801b90046001600160801b0316601481850311156112895761128685838387611e75565b90505b80611292575060015b6003546040516328d09d4760e01b815260009161010090046001600160a01b0316906328d09d47906112ca90899086906004016127f5565b60806040518083038186803b1580156112e257600080fd5b505afa1580156112f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131a919061271e565b9050826113775760055442116113425760405162461bcd60e51b81526004016104ca906129a0565b61135a6005546113558360400151611f54565b611f64565b845467ffffffffffffffff19166001600160401b03821617855592505b61137f6123f6565b60005b6032811015611519578260400151851015801561139f5750868411155b1561147557600184019350829150868411156113e75760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250611470565b6003546040516328d09d4760e01b81526101009091046001600160a01b0316906328d09d479061141d908b9088906004016127f5565b60806040518083038186803b15801561143557600080fd5b505afa158015611449573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146d919061271e565b92505b611511565b42851061148157611519565b6000826040015186039050600081846020015102600f0b8460000151600f0b136114ac5760006114bd565b81846020015102846000015103600f0b5b9050801580156114cc57508886115b156114e3576114da42611f54565b96505050611519565b6001600160a01b038a166000908152600e602090815260408083208a84529091529020555062093a80909401935b600101611382565b505083546001600160801b0316600019929092016001600160401b03908116600160801b029290921767ffffffffffffffff60401b1916600160401b939092169290920217909155505050565b6001600255565b6001600160a01b0382166000908152600b6020526040812080549091600160401b9091046001600160401b031690816115ee574291506115ac42611d2a565b835467ffffffffffffffff19166001600160401b039190911617835560055442116115e95760405162461bcd60e51b81526004016104ca906129a0565b611640565b81420390508361164057600061160383611d2a565b61160c42611d2a565b1490506000620151804261161f42611f54565b0310905081801561162e575080155b1561163d57505050505061053f565b50505b825467ffffffffffffffff60401b1916600160401b426001600160401b0316021783556040516370a0823160e01b81526000906001600160a01b038716906370a08231906116929030906004016127b8565b60206040518083038186803b1580156116aa57600080fd5b505afa1580156116be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e291906127a0565b8454909150600090611705908390600160801b90046001600160801b0316611f70565b90508061171657505050505061053f565b6001600160801b0382111561173d5760405162461bcd60e51b81526004016104ca90612905565b600061174885611d2a565b6001600160a01b0389166000908152600c602052604081209192509081805b601481101561189f578462093a800193508342101561180a578715801561178d57508842145b1561179a578591506117ab565b878942038702816117a757fe5b0491505b6000858152602084905260409020546001600160801b03906117cd9084611f7e565b1161180557600085815260208490526040902080548301905589546001600160801b03600160801b808304821685018216029116178a555b61189f565b8715801561181757508884145b1561182457859150611835565b8789850387028161183157fe5b0491505b6000858152602084905260409020546001600160801b03906118579084611f7e565b1161188f57600085815260208490526040902080548301905589546001600160801b03600160801b808304821685018216029116178a555b9297508793508392600101611767565b507f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1308b868a6040516118d39392919061289b565b60405180910390a15050505050505050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610878908590611f90565b61053f8183146067611e67565b6001600160a01b038083166000908152600f60209081526040808320938516835292905290812054801561198457905061088d565b6001600160a01b038085166000908152600d60209081526040808320549387168352600b9091529020546119c4916001600160401b039081169116611f64565b949350505050565b6001600160a01b03811660009081526001602052604090205460ff161561045857610458336001600160a01b03831614610191611e67565b6006546000611a1242611d2a565b905080821180611a2157504281145b15611a2d575050610d5d565b600360019054906101000a90046001600160a01b03166001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611a7d57600080fd5b505af1158015611a91573d6000803e3d6000fd5b5050505060005b6014811015611b515781831115611aae57611b51565b60035460405163bd85b03960e01b81526101009091046001600160a01b03169063bd85b03990611ae2908690600401612892565b60206040518083038186803b158015611afa57600080fd5b505afa158015611b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3291906127a0565b60008481526007602052604090205562093a8090920191600101611a98565b5050600655565b6001600160a01b0381166000908152600b6020526040812081611b7b858561194f565b6006546001600160a01b0387166000908152600d602052604081205492935091611be291611bc291611bbd9190600160401b90046001600160401b031661207a565b611f54565b8454611bdd90600160401b90046001600160401b0316611d2a565b61207a565b6001600160a01b038087166000908152600c60209081526040808320938b168352600e9091528120929350909190805b6014811015611c7e57848610611c2757611c7e565b600086815260076020526040902054611c3f57611c7e565b60008681526007602090815260408083205486835281842054928890529220540281611c6757fe5b62093a809790970196049190910190600101611c12565b506001600160a01b03808a166000908152600f60209081526040808320938c168352929052208590558015611d1e5785546001600160801b03600160801b80830482168490038216029116178655611ce06001600160a01b0389168a83612086565b7fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de689898388604051611d1594939291906127cc565b60405180910390a15b98975050505050505050565b62093a80908190040290565b6001600160a01b038216600081815260016020908152604091829020805460ff191685151590811790915582519384529083015280517fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c9281900390910190a15050565b60007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611e076120d8565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b031681526020019550505050505060405160208183030381529060405280519060200120905090565b610878848484600019856120dc565b8161053f5761053f81612133565b60008282825b6080811015611f4857818310611e9057611f48565b6003546040516328d09d4760e01b81526002858501810104916000916101009091046001600160a01b0316906328d09d4790611ed2908d9086906004016127f5565b60806040518083038186803b158015611eea57600080fd5b505afa158015611efe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f22919061271e565b905088816040015111611f3757819450611f3e565b6001820393505b5050600101611e7b565b50909695505050505050565b600061088d62093a7f8301611d2a565b80820390821002900390565b600061088a83836001612143565b600082820161088a8482101583611e67565b600080836001600160a01b0316836040518082805190602001908083835b60208310611fcd5780518252601f199092019160209182019101611fae565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461202f576040519150601f19603f3d011682016040523d82523d6000602084013e612034565b606091505b5091509150600082141561204c573d6000803e3d6000fd5b610878815160001480612072575081806020019051602081101561206f57600080fd5b50515b6101a2611e67565b80820390821102900390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526110b7908490611f90565b4690565b60006120e785612159565b90506120fd6120f78783876121a5565b83611e67565b61210c428410156101b8611e67565b5050506001600160a01b039092166000908152602081905260409020805460010190555050565b610458816210905360ea1b6122c3565b60006121528484111583611e67565b5050900390565b6000612163611d9a565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b60006121b9846001600160a01b0316612324565b156122b15760408051630b135d3f60e11b808252600482018681526024830193845285516044840152855191936001600160a01b03891693631626ba7e938993899390929091606490910190602085019080838360005b83811015612228578181015183820152602001612210565b50505050905090810190601f1680156122555780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b15801561227357600080fd5b505afa158015612287573d6000803e3d6000fd5b505050506040513d602081101561229d57600080fd5b50516001600160e01b031916149050610a80565b6122bc84848461232a565b9050610a80565b62461bcd60e51b600090815260206004526007602452600a808404818106603090810160081b958390069590950190829004918206850160101b01602363ffffff0060e086901c160160181b0190930160c81b60445260e882901c90606490fd5b3b151590565b600061233c82516041146101b9611e67565b60008060006020850151925060408501519150606085015160001a9050600060018783868660405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156123b5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611d1e5750876001600160a01b0316816001600160a01b03161498975050505050505050565b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b60008083601f840112612435578081fd5b5081356001600160401b0381111561244b578182fd5b602083019150836020808302850101111561246557600080fd5b9250929050565b8035801515811461048557600080fd5b8051600f81900b811461048557600080fd5b60006020828403121561249f578081fd5b813561088a81612afc565b6000806000604084860312156124be578182fd5b83356124c981612afc565b925060208401356001600160401b038111156124e3578283fd5b6124ef86828701612424565b9497909650939450505050565b600080600060608486031215612510578283fd5b833561251b81612afc565b9250602061252a85820161246c565b925060408501356001600160401b0380821115612545578384fd5b818701915087601f830112612558578384fd5b81358181111561256457fe5b612576601f8201601f19168501612ad9565b9150808252888482850101111561258b578485fd5b808484018584013784848284010152508093505050509250925092565b600080604083850312156125ba578182fd5b82356125c581612afc565b915060208301356125d581612afc565b809150509250929050565b600080604083850312156125f2578182fd5b82356125fd81612afc565b946020939093013593505050565b6000806020838503121561261d578182fd5b82356001600160401b03811115612632578283fd5b61263e85828601612424565b90969095509350505050565b6000806000806040858703121561265f578081fd5b84356001600160401b0380821115612675578283fd5b61268188838901612424565b90965094506020870135915080821115612699578283fd5b506126a687828801612424565b95989497509550505050565b6000602082840312156126c3578081fd5b61088a8261246c565b600080600080608085870312156126e1578182fd5b84356126ec81612afc565b935060208501356126fc81612afc565b925060408501359150606085013561271381612afc565b939692955090935050565b60006080828403121561272f578081fd5b604051608081018181106001600160401b038211171561274b57fe5b6040526127578361247c565b81526127656020840161247c565b602082015260408301516040820152606083015160608201528091505092915050565b600060208284031215612799578081fd5b5035919050565b6000602082840312156127b1578081fd5b5051919050565b6001600160a01b0391909116815260200190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b81811015611f485783516001600160a01b03168352928401929184019160010161282a565b6020808252825182820181905260009190848201906040850190845b81811015611f485783518352928401929184019160010161286b565b901515815260200190565b90815260200190565b6001600160a01b039390931683526020830191909152604082015260600190565b6020808252600d908201526c185b1c9958591e48195e1a5cdd609a1b604082015260600190565b60208082526008908201526708585b1b1bddd95960c21b604082015260600190565b6020808252601e908201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604082015260600190565b60208082526006908201526521747769636560d01b604082015260600190565b602080825260059082015264217a65726f60d81b604082015260600190565b6020808252600b908201526a1bdb9b1e4819985d58d95d60aa1b604082015260600190565b60208082526027908201527f52657761726420646973747269627574696f6e20686173206e6f7420737461726040820152661d1959081e595d60ca1b606082015260800190565b6020808252818101527f43616e6e6f74207374617274206265666f72652063757272656e74207765656b604082015260600190565b6020808252600c908201526b7a65726f206164647265737360a01b604082015260600190565b6020808252601290820152710626040eecacad6e640c8cad8c2f240dac2f60731b604082015260600190565b60208082526028908201527f5a65726f20746f74616c20737570706c7920726573756c747320696e206c6f736040820152677420746f6b656e7360c01b606082015260800190565b6020808252600990820152683737ba1030b236b4b760b91b604082015260600190565b6040518181016001600160401b0381118282101715612af457fe5b604052919050565b6001600160a01b038116811461045857600080fdfea2646970667358221220972c2bb8ecdbe63efa080ae50f636a301051bc328845b00b90454df26829a68764736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101e55760003560e01c806390193b7c1161010f578063de681faf116100a2578063ed3a088711610071578063ed3a0887146103ea578063f213bd8c146103fd578063f851a44014610410578063fcaa54ee14610418576101e5565b8063de681faf146103a7578063e811f44b146103ba578063ece7514d146103cd578063ed24911d146103e2576101e5565b8063be203094116100de578063be20309414610366578063c2c4c5c114610379578063ca31879d14610381578063d3dc4ca114610394576101e5565b806390193b7c1461031a578063905d10ac1461032d578063a1648aa514610340578063acbc142814610353576101e5565b80634c9a47d8116101875780638050a7ee116101565780638050a7ee146102cc57806382aa5ad4146102df578063876e69a1146102e757806388720467146102fa576101e5565b80634c9a47d8146102805780634f3c50901461029357806375829def146102a65780637b8d6221146102b9576101e5565b8063338b5dea116101c3578063338b5dea1461023d5780633902b9bc14610250578063392e53cd14610263578063397bcd4114610278576101e5565b806308b0308a146101ea57806314866e08146102085780632308805b1461021d575b600080fd5b6101f261042b565b6040516101ff91906127b8565b60405180910390f35b61021b61021636600461248e565b61043f565b005b61023061022b36600461248e565b61045b565b6040516101ff9190612892565b61021b61024b3660046125e0565b61048a565b61021b61025e36600461248e565b610543565b61026b61058e565b6040516101ff9190612887565b6101f2610597565b61021b61028e3660046125e0565b6105a6565b6102306102a1366004612788565b610633565b61021b6102b436600461248e565b610645565b61021b6102c736600461264a565b6106df565b6102306102da3660046125a8565b61087e565b610230610893565b6102306102f536600461248e565b610899565b61030d6103083660046124aa565b6108c4565b6040516101ff919061284f565b61023061032836600461248e565b610a87565b61021b61033b36600461260b565b610aa2565b61026b61034e36600461248e565b610b35565b61023061036136600461248e565b610b53565b61021b6103743660046126cc565b610b7e565b61021b610d45565b61023061038f3660046125a8565b610d5f565b6102306103a23660046125e0565b610e41565b6102306103b53660046125e0565b610e69565b61021b6103c83660046126b2565b610e91565b6103d5610e9b565b6040516101ff919061280e565b610230610efd565b61026b6103f836600461248e565b610f0c565b61021b61040b36600461260b565b610f21565b6101f26110bc565b61021b6104263660046124fc565b6110cb565b60035461010090046001600160a01b031690565b610447611159565b61045081611170565b610458611566565b50565b6001600160a01b0381166000908152600b6020526040902054600160801b90046001600160801b03165b919050565b610492611159565b6001600160a01b0382166000908152600a602052604090205460ff166104d35760405162461bcd60e51b81526004016104ca906128e3565b60405180910390fd5b6104de82600061156d565b6104f36001600160a01b0383163330846118e8565b6104fe82600161156d565b7f95bf5847357310d24f8d03d8bad76c8ee329dfd3a3cb200df21c7bd1619e93bd828260405161052f9291906127f5565b60405180910390a161053f611566565b5050565b61054b611159565b6001600160a01b0381166000908152600a602052604090205460ff166105835760405162461bcd60e51b81526004016104ca906128e3565b61045081600161156d565b60035460ff1681565b6004546001600160a01b031681565b6001600160a01b0382166000908152600a602052604090205460ff166105de5760405162461bcd60e51b81526004016104ca906128e3565b6004546001600160a01b031633146106085760405162461bcd60e51b81526004016104ca9061297b565b61061382600061156d565b6106286001600160a01b0383163330846118e8565b61053f82600161156d565b60009081526007602052604090205490565b6008546001600160a01b0316331461066f5760405162461bcd60e51b81526004016104ca90612ab6565b6001600160a01b0381166106955760405162461bcd60e51b81526004016104ca90612a1c565b600880546001600160a01b0319166001600160a01b0383169081179091556040517f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c90600090a250565b6106e7611159565b6106f18382611942565b8260005b8181101561086e57600a600087878481811061070d57fe5b9050602002016020810190610722919061248e565b6001600160a01b0316815260208101919091526040016000205460ff1661075b5760405162461bcd60e51b81526004016104ca906128e3565b61078686868381811061076a57fe5b905060200201602081019061077f919061248e565b600061156d565b6107d0333086868581811061079757fe5b905060200201358989868181106107aa57fe5b90506020020160208101906107bf919061248e565b6001600160a01b03169291906118e8565b6107fb8686838181106107df57fe5b90506020020160208101906107f4919061248e565b600161156d565b7f95bf5847357310d24f8d03d8bad76c8ee329dfd3a3cb200df21c7bd1619e93bd86868381811061082857fe5b905060200201602081019061083d919061248e565b85858481811061084957fe5b9050602002013560405161085e9291906127f5565b60405180910390a16001016106f5565b5050610878611566565b50505050565b600061088a838361194f565b90505b92915050565b60065490565b6001600160a01b03166000908152600d6020526040902054600160401b90046001600160401b031690565b60606108ce611159565b836108d8816119cc565b6108e0611a04565b6108e985611170565b826000816001600160401b038111801561090257600080fd5b5060405190808252806020026020018201604052801561092c578160200160208202803683370190505b50905060005b82811015610a7357600a600088888481811061094a57fe5b905060200201602081019061095f919061248e565b6001600160a01b0316815260208101919091526040016000205460ff166109985760405162461bcd60e51b81526004016104ca906128e3565b6109a787878381811061076a57fe5b6109d1888888848181106109b757fe5b90506020020160208101906109cc919061248e565b611b58565b8282815181106109dd57fe5b60209081029190910101526004546001600160a01b031663c7b56abe888884818110610a0557fe5b9050602002016020810190610a1a919061248e565b6040518263ffffffff1660e01b8152600401610a3691906127b8565b600060405180830381600087803b158015610a5057600080fd5b505af1158015610a64573d6000803e3d6000fd5b50505050806001019050610932565b5092505050610a80611566565b9392505050565b6001600160a01b031660009081526020819052604090205490565b610aaa611159565b8060005b81811015610b2b57600a6000858584818110610ac657fe5b9050602002016020810190610adb919061248e565b6001600160a01b0316815260208101919091526040016000205460ff16610b145760405162461bcd60e51b81526004016104ca906128e3565b610b238484838181106107df57fe5b600101610aae565b505061053f611566565b6001600160a01b031660009081526001602052604090205460ff1690565b6001600160a01b03166000908152600b6020526040902054600160401b90046001600160401b031690565b60035460ff1615610ba15760405162461bcd60e51b81526004016104ca9061293c565b6003805460ff191660011790556001600160a01b03811615801590610bce57506001600160a01b03831615155b610bea5760405162461bcd60e51b81526004016104ca9061295c565b600880546001600160a01b038084166001600160a01b0319928316179092556004805486841692169190911790556003805491861661010002610100600160a81b0319909216919091179055610c3f82611d2a565b91506000610c4c42611d2a565b905080831015610c6e5760405162461bcd60e51b81526004016104ca906129e7565b80625c490001831115610c935760405162461bcd60e51b81526004016104ca90612a42565b80831415610d375760405163bd85b03960e01b81526000906001600160a01b0387169063bd85b03990610cca908590600401612892565b60206040518083038186803b158015610ce257600080fd5b505afa158015610cf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1a91906127a0565b11610d375760405162461bcd60e51b81526004016104ca90612a6e565b505060058190556006555050565b610d4d611159565b610d55611a04565b610d5d611566565b565b6000610d69611159565b82610d73816119cc565b6001600160a01b0383166000908152600a602052604090205460ff16610dab5760405162461bcd60e51b81526004016104ca906128e3565b610db3611a04565b610dbc84611170565b610dc783600061156d565b6000610dd38585611b58565b600480546040516363dab55f60e11b81529293506001600160a01b03169163c7b56abe91610e03918891016127b8565b600060405180830381600087803b158015610e1d57600080fd5b505af1158015610e31573d6000803e3d6000fd5b509294505050505061088d611566565b6001600160a01b03919091166000908152600c60209081526040808320938352929052205490565b6001600160a01b03919091166000908152600e60209081526040808320938352929052205490565b6104583382611d36565b60606009805480602002602001604051908101604052809291908181526020018280548015610ef357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ed5575b5050505050905090565b6000610f07611d9a565b905090565b600a6020526000908152604090205460ff1681565b6008546001600160a01b03163314610f4b5760405162461bcd60e51b81526004016104ca90612ab6565b60005b818110156110b757600a6000848484818110610f6657fe5b9050602002016020810190610f7b919061248e565b6001600160a01b0316815260208101919091526040016000205460ff1615610fb55760405162461bcd60e51b81526004016104ca906128bc565b6001600a6000858585818110610fc757fe5b9050602002016020810190610fdc919061248e565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055600983838381811061101257fe5b9050602002016020810190611027919061248e565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905582828281811061106557fe5b905060200201602081019061107a919061248e565b6001600160a01b03167f784c8f4dbf0ffedd6e72c76501c545a70f8b203b30a26ce542bf92ba87c248a460405160405180910390a2600101610f4e565b505050565b6008546001600160a01b031681565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a9084846110f987610a87565b60405160200180858152602001846001600160a01b03168152602001831515815260200182815260200194505050505060405160208183030381529060405280519060200120905061114f8482846101f8611e58565b6108788484611d36565b61116a600280541415610190611e67565b60028055565b60035460405163010ae75760e01b815260009161010090046001600160a01b03169063010ae757906111a69085906004016127b8565b60206040518083038186803b1580156111be57600080fd5b505afa1580156111d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f691906127a0565b9050806112035750610458565b6001600160a01b0382166000908152600d6020526040812080549091600160401b9091046001600160401b0316908161124c5761124585600554600087611e75565b9050611289565b42821061125c5750505050610458565b508154600160801b90046001600160801b0316601481850311156112895761128685838387611e75565b90505b80611292575060015b6003546040516328d09d4760e01b815260009161010090046001600160a01b0316906328d09d47906112ca90899086906004016127f5565b60806040518083038186803b1580156112e257600080fd5b505afa1580156112f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131a919061271e565b9050826113775760055442116113425760405162461bcd60e51b81526004016104ca906129a0565b61135a6005546113558360400151611f54565b611f64565b845467ffffffffffffffff19166001600160401b03821617855592505b61137f6123f6565b60005b6032811015611519578260400151851015801561139f5750868411155b1561147557600184019350829150868411156113e75760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250611470565b6003546040516328d09d4760e01b81526101009091046001600160a01b0316906328d09d479061141d908b9088906004016127f5565b60806040518083038186803b15801561143557600080fd5b505afa158015611449573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146d919061271e565b92505b611511565b42851061148157611519565b6000826040015186039050600081846020015102600f0b8460000151600f0b136114ac5760006114bd565b81846020015102846000015103600f0b5b9050801580156114cc57508886115b156114e3576114da42611f54565b96505050611519565b6001600160a01b038a166000908152600e602090815260408083208a84529091529020555062093a80909401935b600101611382565b505083546001600160801b0316600019929092016001600160401b03908116600160801b029290921767ffffffffffffffff60401b1916600160401b939092169290920217909155505050565b6001600255565b6001600160a01b0382166000908152600b6020526040812080549091600160401b9091046001600160401b031690816115ee574291506115ac42611d2a565b835467ffffffffffffffff19166001600160401b039190911617835560055442116115e95760405162461bcd60e51b81526004016104ca906129a0565b611640565b81420390508361164057600061160383611d2a565b61160c42611d2a565b1490506000620151804261161f42611f54565b0310905081801561162e575080155b1561163d57505050505061053f565b50505b825467ffffffffffffffff60401b1916600160401b426001600160401b0316021783556040516370a0823160e01b81526000906001600160a01b038716906370a08231906116929030906004016127b8565b60206040518083038186803b1580156116aa57600080fd5b505afa1580156116be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e291906127a0565b8454909150600090611705908390600160801b90046001600160801b0316611f70565b90508061171657505050505061053f565b6001600160801b0382111561173d5760405162461bcd60e51b81526004016104ca90612905565b600061174885611d2a565b6001600160a01b0389166000908152600c602052604081209192509081805b601481101561189f578462093a800193508342101561180a578715801561178d57508842145b1561179a578591506117ab565b878942038702816117a757fe5b0491505b6000858152602084905260409020546001600160801b03906117cd9084611f7e565b1161180557600085815260208490526040902080548301905589546001600160801b03600160801b808304821685018216029116178a555b61189f565b8715801561181757508884145b1561182457859150611835565b8789850387028161183157fe5b0491505b6000858152602084905260409020546001600160801b03906118579084611f7e565b1161188f57600085815260208490526040902080548301905589546001600160801b03600160801b808304821685018216029116178a555b9297508793508392600101611767565b507f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1308b868a6040516118d39392919061289b565b60405180910390a15050505050505050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610878908590611f90565b61053f8183146067611e67565b6001600160a01b038083166000908152600f60209081526040808320938516835292905290812054801561198457905061088d565b6001600160a01b038085166000908152600d60209081526040808320549387168352600b9091529020546119c4916001600160401b039081169116611f64565b949350505050565b6001600160a01b03811660009081526001602052604090205460ff161561045857610458336001600160a01b03831614610191611e67565b6006546000611a1242611d2a565b905080821180611a2157504281145b15611a2d575050610d5d565b600360019054906101000a90046001600160a01b03166001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611a7d57600080fd5b505af1158015611a91573d6000803e3d6000fd5b5050505060005b6014811015611b515781831115611aae57611b51565b60035460405163bd85b03960e01b81526101009091046001600160a01b03169063bd85b03990611ae2908690600401612892565b60206040518083038186803b158015611afa57600080fd5b505afa158015611b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3291906127a0565b60008481526007602052604090205562093a8090920191600101611a98565b5050600655565b6001600160a01b0381166000908152600b6020526040812081611b7b858561194f565b6006546001600160a01b0387166000908152600d602052604081205492935091611be291611bc291611bbd9190600160401b90046001600160401b031661207a565b611f54565b8454611bdd90600160401b90046001600160401b0316611d2a565b61207a565b6001600160a01b038087166000908152600c60209081526040808320938b168352600e9091528120929350909190805b6014811015611c7e57848610611c2757611c7e565b600086815260076020526040902054611c3f57611c7e565b60008681526007602090815260408083205486835281842054928890529220540281611c6757fe5b62093a809790970196049190910190600101611c12565b506001600160a01b03808a166000908152600f60209081526040808320938c168352929052208590558015611d1e5785546001600160801b03600160801b80830482168490038216029116178655611ce06001600160a01b0389168a83612086565b7fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de689898388604051611d1594939291906127cc565b60405180910390a15b98975050505050505050565b62093a80908190040290565b6001600160a01b038216600081815260016020908152604091829020805460ff191685151590811790915582519384529083015280517fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c9281900390910190a15050565b60007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611e076120d8565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b031681526020019550505050505060405160208183030381529060405280519060200120905090565b610878848484600019856120dc565b8161053f5761053f81612133565b60008282825b6080811015611f4857818310611e9057611f48565b6003546040516328d09d4760e01b81526002858501810104916000916101009091046001600160a01b0316906328d09d4790611ed2908d9086906004016127f5565b60806040518083038186803b158015611eea57600080fd5b505afa158015611efe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f22919061271e565b905088816040015111611f3757819450611f3e565b6001820393505b5050600101611e7b565b50909695505050505050565b600061088d62093a7f8301611d2a565b80820390821002900390565b600061088a83836001612143565b600082820161088a8482101583611e67565b600080836001600160a01b0316836040518082805190602001908083835b60208310611fcd5780518252601f199092019160209182019101611fae565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461202f576040519150601f19603f3d011682016040523d82523d6000602084013e612034565b606091505b5091509150600082141561204c573d6000803e3d6000fd5b610878815160001480612072575081806020019051602081101561206f57600080fd5b50515b6101a2611e67565b80820390821102900390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526110b7908490611f90565b4690565b60006120e785612159565b90506120fd6120f78783876121a5565b83611e67565b61210c428410156101b8611e67565b5050506001600160a01b039092166000908152602081905260409020805460010190555050565b610458816210905360ea1b6122c3565b60006121528484111583611e67565b5050900390565b6000612163611d9a565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b60006121b9846001600160a01b0316612324565b156122b15760408051630b135d3f60e11b808252600482018681526024830193845285516044840152855191936001600160a01b03891693631626ba7e938993899390929091606490910190602085019080838360005b83811015612228578181015183820152602001612210565b50505050905090810190601f1680156122555780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b15801561227357600080fd5b505afa158015612287573d6000803e3d6000fd5b505050506040513d602081101561229d57600080fd5b50516001600160e01b031916149050610a80565b6122bc84848461232a565b9050610a80565b62461bcd60e51b600090815260206004526007602452600a808404818106603090810160081b958390069590950190829004918206850160101b01602363ffffff0060e086901c160160181b0190930160c81b60445260e882901c90606490fd5b3b151590565b600061233c82516041146101b9611e67565b60008060006020850151925060408501519150606085015160001a9050600060018783868660405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156123b5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611d1e5750876001600160a01b0316816001600160a01b03161498975050505050505050565b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b60008083601f840112612435578081fd5b5081356001600160401b0381111561244b578182fd5b602083019150836020808302850101111561246557600080fd5b9250929050565b8035801515811461048557600080fd5b8051600f81900b811461048557600080fd5b60006020828403121561249f578081fd5b813561088a81612afc565b6000806000604084860312156124be578182fd5b83356124c981612afc565b925060208401356001600160401b038111156124e3578283fd5b6124ef86828701612424565b9497909650939450505050565b600080600060608486031215612510578283fd5b833561251b81612afc565b9250602061252a85820161246c565b925060408501356001600160401b0380821115612545578384fd5b818701915087601f830112612558578384fd5b81358181111561256457fe5b612576601f8201601f19168501612ad9565b9150808252888482850101111561258b578485fd5b808484018584013784848284010152508093505050509250925092565b600080604083850312156125ba578182fd5b82356125c581612afc565b915060208301356125d581612afc565b809150509250929050565b600080604083850312156125f2578182fd5b82356125fd81612afc565b946020939093013593505050565b6000806020838503121561261d578182fd5b82356001600160401b03811115612632578283fd5b61263e85828601612424565b90969095509350505050565b6000806000806040858703121561265f578081fd5b84356001600160401b0380821115612675578283fd5b61268188838901612424565b90965094506020870135915080821115612699578283fd5b506126a687828801612424565b95989497509550505050565b6000602082840312156126c3578081fd5b61088a8261246c565b600080600080608085870312156126e1578182fd5b84356126ec81612afc565b935060208501356126fc81612afc565b925060408501359150606085013561271381612afc565b939692955090935050565b60006080828403121561272f578081fd5b604051608081018181106001600160401b038211171561274b57fe5b6040526127578361247c565b81526127656020840161247c565b602082015260408301516040820152606083015160608201528091505092915050565b600060208284031215612799578081fd5b5035919050565b6000602082840312156127b1578081fd5b5051919050565b6001600160a01b0391909116815260200190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b81811015611f485783516001600160a01b03168352928401929184019160010161282a565b6020808252825182820181905260009190848201906040850190845b81811015611f485783518352928401929184019160010161286b565b901515815260200190565b90815260200190565b6001600160a01b039390931683526020830191909152604082015260600190565b6020808252600d908201526c185b1c9958591e48195e1a5cdd609a1b604082015260600190565b60208082526008908201526708585b1b1bddd95960c21b604082015260600190565b6020808252601e908201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604082015260600190565b60208082526006908201526521747769636560d01b604082015260600190565b602080825260059082015264217a65726f60d81b604082015260600190565b6020808252600b908201526a1bdb9b1e4819985d58d95d60aa1b604082015260600190565b60208082526027908201527f52657761726420646973747269627574696f6e20686173206e6f7420737461726040820152661d1959081e595d60ca1b606082015260800190565b6020808252818101527f43616e6e6f74207374617274206265666f72652063757272656e74207765656b604082015260600190565b6020808252600c908201526b7a65726f206164647265737360a01b604082015260600190565b6020808252601290820152710626040eecacad6e640c8cad8c2f240dac2f60731b604082015260600190565b60208082526028908201527f5a65726f20746f74616c20737570706c7920726573756c747320696e206c6f736040820152677420746f6b656e7360c01b606082015260800190565b6020808252600990820152683737ba1030b236b4b760b91b604082015260600190565b6040518181016001600160401b0381118282101715612af457fe5b604052919050565b6001600160a01b038116811461045857600080fdfea2646970667358221220972c2bb8ecdbe63efa080ae50f636a301051bc328845b00b90454df26829a68764736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/RewardFaucet.json b/apps/api/src/app/hardhat/export/RewardFaucet.json new file mode 100644 index 000000000..26d7680f1 --- /dev/null +++ b/apps/api/src/app/hardhat/export/RewardFaucet.json @@ -0,0 +1,324 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "RewardFaucet", + "sourceName": "contracts/RewardFaucet.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "weekStart", + "type": "uint256" + } + ], + "name": "DistributePast", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "weeksCount", + "type": "uint256" + } + ], + "name": "ExactWeekDistribution", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "moveAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "pastWeekStart", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nextWeekStart", + "type": "uint256" + } + ], + "name": "MovePastRewards", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "weeksCount", + "type": "uint256" + } + ], + "name": "WeeksDistributions", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weeksCount", + "type": "uint256" + } + ], + "name": "depositEqualWeeksPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weekTimeStamp", + "type": "uint256" + } + ], + "name": "depositExactWeek", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "distributePastRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "pointOfWeek", + "type": "uint256" + } + ], + "name": "getTokenWeekAmounts", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "weeksCount", + "type": "uint256" + } + ], + "name": "getUpcomingRewardsForNWeeks", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_rewardDistributor", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "pastWeekTimestamp", + "type": "uint256" + } + ], + "name": "movePastRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rewardDistributor", + "outputs": [ + { + "internalType": "contract IRewardDistributor", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "weekStart", + "type": "uint256" + } + ], + "name": "tokenWeekAmounts", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "totalTokenRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "rewardAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506001600055611315806100256000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806367b870111161007157806367b8701114610139578063871451781461014c578063acc2166a14610177578063c4d66de8146101a7578063c7b56abe146101ba578063cdcea6ad146101cd57600080fd5b80632a419597146100ae5780632d9cd3bb146100c357806336bcebb1146100d6578063392e53cd146101095780635530d7bd14610126575b600080fd5b6100c16100bc3660046110b4565b6101ed565b005b6100c16100d13660046110b4565b6104f0565b6100f66100e43660046110e7565b60026020526000908152604090205481565b6040519081526020015b60405180910390f35b6001546101169060ff1681565b6040519015158152602001610100565b6100c1610134366004611109565b61076c565b6100f6610147366004611109565b610876565b6100f661015a366004611109565b600360209081526000928352604080842090915290825290205481565b60015461018f9061010090046001600160a01b031681565b6040516001600160a01b039091168152602001610100565b6100c16101b53660046110e7565b6108af565b6100c16101c83660046110e7565b610952565b6101e06101db366004611109565b610b28565b6040516101009190611133565b6101f5610c00565b600081118015610206575060688111155b61023f5760405162461bcd60e51b8152602060048201526005602482015264217765656b60d81b60448201526064015b60405180910390fd5b6001600160a01b0383166000818152600260205260408082205490516370a0823160e01b8152306004820152919290916370a0823190602401602060405180830381865afa158015610295573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b99190611177565b6102c391906111a6565b905060008082116102d457836102de565b6102de82856111b9565b905060006102ec84836111cc565b9050836001146103fa57600061030142610c59565b905060025b8581116103c0578581036103715761031f6001876111a6565b61032990846111ee565b61033390856111a6565b6001600160a01b0389166000908152600360209081526040808320868452909152812080549091906103669084906111b9565b909155506103c09050565b6001600160a01b0388166000908152600360209081526040808320858452909152812080548592906103a49084906111b9565b90915550506001016103b962093a80836111b9565b9150610306565b506103cb82846111a6565b6001600160a01b038816600090815260026020526040812080549091906103f39084906111b9565b9091555050505b61040f6001600160a01b038716333088610c70565b6001546001600160a01b036101009091048116906104309088168284610ce1565b60405163099348fb60e31b81526001600160a01b03888116600483015260248201849052821690634c9a47d890604401600060405180830381600087803b15801561047a57600080fd5b505af115801561048e573d6000803e3d6000fd5b5050604080516001600160a01b038b168152602081018790529081018890527f4088fafdc9c718bca399ea616e6c39e860759e8cc97fbda29803d42b0bc6a2249250606001905060405180910390a1505050506104eb6001600055565b505050565b6104f8610c00565b61050142610d70565b811015801561051d5750610519426303bfc4006111b9565b8111155b6105545760405162461bcd60e51b8152602060048201526008602482015267626164207765656b60c01b6044820152606401610236565b6001600160a01b0383166000818152600260205260408082205490516370a0823160e01b8152306004820152919290916370a0823190602401602060405180830381865afa1580156105aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ce9190611177565b6105d891906111a6565b905060008082116105e957836105f3565b6105f382856111b9565b905061060a6001600160a01b038616333087610c70565b600061061584610d70565b905061062042610d70565b81036106af576001546001600160a01b036101009091048116906106479088168285610ce1565b60405163099348fb60e31b81526001600160a01b03888116600483015260248201859052821690634c9a47d890604401600060405180830381600087803b15801561069157600080fd5b505af11580156106a5573d6000803e3d6000fd5b5050505050610715565b6001600160a01b0386166000908152600360209081526040808320848452909152812080548492906106e29084906111b9565b90915550506001600160a01b0386166000908152600260205260408120805484929061070f9084906111b9565b90915550505b604080516001600160a01b0388168152602081018490529081018290527fb86a33c5016189ecd35a2699118bc2fe7c716f81d4e837eef84dee0bb67875f59060600160405180910390a15050506104eb6001600055565b600061077782610d70565b905062530e8061078642610d70565b61079091906111a6565b81106107c95760405162461bcd60e51b8152602060048201526008602482015267216f75746461746560c01b6044820152606401610236565b60006107d442610c59565b6001600160a01b0385166000908152600360209081526040808320868452909152808220805490839055838352908220805493945090928392906108199084906111b9565b9091555050604080516001600160a01b038716815260208101839052908101849052606081018390527f6e9630aa131b46b81742f09d3aea83da20d184c4dbe662050e8d6d2d554503929060800160405180910390a15050505050565b60008061088283610d70565b6001600160a01b038516600090815260036020908152604080832093835292905220549150505b92915050565b60015460ff16156108eb5760405162461bcd60e51b815260206004820152600660248201526521747769636560d01b6044820152606401610236565b6001600160a01b0381166109295760405162461bcd60e51b8152602060048201526005602482015264217a65726f60d81b6044820152606401610236565b600180546001600160a01b03909216610100026001600160a81b03199092169190911781179055565b6001600160a01b03811660009081526002602052604081205490036109745750565b600061097f42610d70565b90506000805b600a811015610a20576001600160a01b0384166000908152600360209081526040808320868452909152812054908190036109cf576109c762093a80856111a6565b935050610a10565b6001600160a01b03851660009081526003602090815260408083208784529091528120556109fd81846111b9565b9250610a0c62093a80856111a6565b9350505b610a1981611205565b9050610985565b5080156104eb576001600160a01b03831660009081526002602052604081208054839290610a4f9084906111a6565b90915550506001546001600160a01b03610100909104811690610a759085168284610ce1565b60405163099348fb60e31b81526001600160a01b03858116600483015260248201849052821690634c9a47d890604401600060405180830381600087803b158015610abf57600080fd5b505af1158015610ad3573d6000803e3d6000fd5b5050604080516001600160a01b0388168152602081018690529081018690527f33d07963ee4e58f177134d0a37785787f0056ee388c04e4aff075e61e2856d6c9250606001905060405180910390a150505050565b60606000610b3542610d70565b905060008367ffffffffffffffff811115610b5257610b5261121e565b604051908082528060200260200182016040528015610b7b578160200160208202803683370190505b50905060005b84811015610bf7576001600160a01b038616600090815260036020526040812090610baf8362093a806111ee565b610bb990866111b9565b815260200190815260200160002054828281518110610bda57610bda611234565b602090810291909101015280610bef81611205565b915050610b81565b50949350505050565b600260005403610c525760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610236565b6002600055565b60006108a9610c6b8362093a806111b9565b610d70565b6040516001600160a01b0380851660248301528316604482015260648101829052610cdb9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152610d8c565b50505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610d328482610e61565b610cdb576040516001600160a01b038416602482015260006044820152610d6690859063095ea7b360e01b90606401610ca4565b610cdb8482610d8c565b6000610d7f62093a80836111cc565b6108a99062093a806111ee565b6000610de1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316610f089092919063ffffffff16565b9050805160001480610e02575080806020019051810190610e02919061124a565b6104eb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610236565b6000806000846001600160a01b031684604051610e7e9190611290565b6000604051808303816000865af19150503d8060008114610ebb576040519150601f19603f3d011682016040523d82523d6000602084013e610ec0565b606091505b5091509150818015610eea575080511580610eea575080806020019051810190610eea919061124a565b8015610eff57506001600160a01b0385163b15155b95945050505050565b6060610f178484600085610f1f565b949350505050565b606082471015610f805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610236565b600080866001600160a01b03168587604051610f9c9190611290565b60006040518083038185875af1925050503d8060008114610fd9576040519150601f19603f3d011682016040523d82523d6000602084013e610fde565b606091505b5091509150610fef87838387610ffa565b979650505050505050565b60608315611069578251600003611062576001600160a01b0385163b6110625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610236565b5081610f17565b610f17838381511561107e5781518083602001fd5b8060405162461bcd60e51b815260040161023691906112ac565b80356001600160a01b03811681146110af57600080fd5b919050565b6000806000606084860312156110c957600080fd5b6110d284611098565b95602085013595506040909401359392505050565b6000602082840312156110f957600080fd5b61110282611098565b9392505050565b6000806040838503121561111c57600080fd5b61112583611098565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b8181101561116b5783518352928401929184019160010161114f565b50909695505050505050565b60006020828403121561118957600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156108a9576108a9611190565b808201808211156108a9576108a9611190565b6000826111e957634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176108a9576108a9611190565b60006001820161121757611217611190565b5060010190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561125c57600080fd5b8151801515811461110257600080fd5b60005b8381101561128757818101518382015260200161126f565b50506000910152565b600082516112a281846020870161126c565b9190910192915050565b60208152600082518060208401526112cb81604085016020870161126c565b601f01601f1916919091016040019291505056fea264697066735822122000f918e3a59af92e2a14d9123b3fe1755ca0de77c365c4e3d6bd71bea319dedd64736f6c63430008120033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c806367b870111161007157806367b8701114610139578063871451781461014c578063acc2166a14610177578063c4d66de8146101a7578063c7b56abe146101ba578063cdcea6ad146101cd57600080fd5b80632a419597146100ae5780632d9cd3bb146100c357806336bcebb1146100d6578063392e53cd146101095780635530d7bd14610126575b600080fd5b6100c16100bc3660046110b4565b6101ed565b005b6100c16100d13660046110b4565b6104f0565b6100f66100e43660046110e7565b60026020526000908152604090205481565b6040519081526020015b60405180910390f35b6001546101169060ff1681565b6040519015158152602001610100565b6100c1610134366004611109565b61076c565b6100f6610147366004611109565b610876565b6100f661015a366004611109565b600360209081526000928352604080842090915290825290205481565b60015461018f9061010090046001600160a01b031681565b6040516001600160a01b039091168152602001610100565b6100c16101b53660046110e7565b6108af565b6100c16101c83660046110e7565b610952565b6101e06101db366004611109565b610b28565b6040516101009190611133565b6101f5610c00565b600081118015610206575060688111155b61023f5760405162461bcd60e51b8152602060048201526005602482015264217765656b60d81b60448201526064015b60405180910390fd5b6001600160a01b0383166000818152600260205260408082205490516370a0823160e01b8152306004820152919290916370a0823190602401602060405180830381865afa158015610295573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b99190611177565b6102c391906111a6565b905060008082116102d457836102de565b6102de82856111b9565b905060006102ec84836111cc565b9050836001146103fa57600061030142610c59565b905060025b8581116103c0578581036103715761031f6001876111a6565b61032990846111ee565b61033390856111a6565b6001600160a01b0389166000908152600360209081526040808320868452909152812080549091906103669084906111b9565b909155506103c09050565b6001600160a01b0388166000908152600360209081526040808320858452909152812080548592906103a49084906111b9565b90915550506001016103b962093a80836111b9565b9150610306565b506103cb82846111a6565b6001600160a01b038816600090815260026020526040812080549091906103f39084906111b9565b9091555050505b61040f6001600160a01b038716333088610c70565b6001546001600160a01b036101009091048116906104309088168284610ce1565b60405163099348fb60e31b81526001600160a01b03888116600483015260248201849052821690634c9a47d890604401600060405180830381600087803b15801561047a57600080fd5b505af115801561048e573d6000803e3d6000fd5b5050604080516001600160a01b038b168152602081018790529081018890527f4088fafdc9c718bca399ea616e6c39e860759e8cc97fbda29803d42b0bc6a2249250606001905060405180910390a1505050506104eb6001600055565b505050565b6104f8610c00565b61050142610d70565b811015801561051d5750610519426303bfc4006111b9565b8111155b6105545760405162461bcd60e51b8152602060048201526008602482015267626164207765656b60c01b6044820152606401610236565b6001600160a01b0383166000818152600260205260408082205490516370a0823160e01b8152306004820152919290916370a0823190602401602060405180830381865afa1580156105aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ce9190611177565b6105d891906111a6565b905060008082116105e957836105f3565b6105f382856111b9565b905061060a6001600160a01b038616333087610c70565b600061061584610d70565b905061062042610d70565b81036106af576001546001600160a01b036101009091048116906106479088168285610ce1565b60405163099348fb60e31b81526001600160a01b03888116600483015260248201859052821690634c9a47d890604401600060405180830381600087803b15801561069157600080fd5b505af11580156106a5573d6000803e3d6000fd5b5050505050610715565b6001600160a01b0386166000908152600360209081526040808320848452909152812080548492906106e29084906111b9565b90915550506001600160a01b0386166000908152600260205260408120805484929061070f9084906111b9565b90915550505b604080516001600160a01b0388168152602081018490529081018290527fb86a33c5016189ecd35a2699118bc2fe7c716f81d4e837eef84dee0bb67875f59060600160405180910390a15050506104eb6001600055565b600061077782610d70565b905062530e8061078642610d70565b61079091906111a6565b81106107c95760405162461bcd60e51b8152602060048201526008602482015267216f75746461746560c01b6044820152606401610236565b60006107d442610c59565b6001600160a01b0385166000908152600360209081526040808320868452909152808220805490839055838352908220805493945090928392906108199084906111b9565b9091555050604080516001600160a01b038716815260208101839052908101849052606081018390527f6e9630aa131b46b81742f09d3aea83da20d184c4dbe662050e8d6d2d554503929060800160405180910390a15050505050565b60008061088283610d70565b6001600160a01b038516600090815260036020908152604080832093835292905220549150505b92915050565b60015460ff16156108eb5760405162461bcd60e51b815260206004820152600660248201526521747769636560d01b6044820152606401610236565b6001600160a01b0381166109295760405162461bcd60e51b8152602060048201526005602482015264217a65726f60d81b6044820152606401610236565b600180546001600160a01b03909216610100026001600160a81b03199092169190911781179055565b6001600160a01b03811660009081526002602052604081205490036109745750565b600061097f42610d70565b90506000805b600a811015610a20576001600160a01b0384166000908152600360209081526040808320868452909152812054908190036109cf576109c762093a80856111a6565b935050610a10565b6001600160a01b03851660009081526003602090815260408083208784529091528120556109fd81846111b9565b9250610a0c62093a80856111a6565b9350505b610a1981611205565b9050610985565b5080156104eb576001600160a01b03831660009081526002602052604081208054839290610a4f9084906111a6565b90915550506001546001600160a01b03610100909104811690610a759085168284610ce1565b60405163099348fb60e31b81526001600160a01b03858116600483015260248201849052821690634c9a47d890604401600060405180830381600087803b158015610abf57600080fd5b505af1158015610ad3573d6000803e3d6000fd5b5050604080516001600160a01b0388168152602081018690529081018690527f33d07963ee4e58f177134d0a37785787f0056ee388c04e4aff075e61e2856d6c9250606001905060405180910390a150505050565b60606000610b3542610d70565b905060008367ffffffffffffffff811115610b5257610b5261121e565b604051908082528060200260200182016040528015610b7b578160200160208202803683370190505b50905060005b84811015610bf7576001600160a01b038616600090815260036020526040812090610baf8362093a806111ee565b610bb990866111b9565b815260200190815260200160002054828281518110610bda57610bda611234565b602090810291909101015280610bef81611205565b915050610b81565b50949350505050565b600260005403610c525760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610236565b6002600055565b60006108a9610c6b8362093a806111b9565b610d70565b6040516001600160a01b0380851660248301528316604482015260648101829052610cdb9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152610d8c565b50505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610d328482610e61565b610cdb576040516001600160a01b038416602482015260006044820152610d6690859063095ea7b360e01b90606401610ca4565b610cdb8482610d8c565b6000610d7f62093a80836111cc565b6108a99062093a806111ee565b6000610de1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316610f089092919063ffffffff16565b9050805160001480610e02575080806020019051810190610e02919061124a565b6104eb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610236565b6000806000846001600160a01b031684604051610e7e9190611290565b6000604051808303816000865af19150503d8060008114610ebb576040519150601f19603f3d011682016040523d82523d6000602084013e610ec0565b606091505b5091509150818015610eea575080511580610eea575080806020019051810190610eea919061124a565b8015610eff57506001600160a01b0385163b15155b95945050505050565b6060610f178484600085610f1f565b949350505050565b606082471015610f805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610236565b600080866001600160a01b03168587604051610f9c9190611290565b60006040518083038185875af1925050503d8060008114610fd9576040519150601f19603f3d011682016040523d82523d6000602084013e610fde565b606091505b5091509150610fef87838387610ffa565b979650505050505050565b60608315611069578251600003611062576001600160a01b0385163b6110625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610236565b5081610f17565b610f17838381511561107e5781518083602001fd5b8060405162461bcd60e51b815260040161023691906112ac565b80356001600160a01b03811681146110af57600080fd5b919050565b6000806000606084860312156110c957600080fd5b6110d284611098565b95602085013595506040909401359392505050565b6000602082840312156110f957600080fd5b61110282611098565b9392505050565b6000806040838503121561111c57600080fd5b61112583611098565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b8181101561116b5783518352928401929184019160010161114f565b50909695505050505050565b60006020828403121561118957600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156108a9576108a9611190565b808201808211156108a9576108a9611190565b6000826111e957634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176108a9576108a9611190565b60006001820161121757611217611190565b5060010190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561125c57600080fd5b8151801515811461110257600080fd5b60005b8381101561128757818101518382015260200161126f565b50506000910152565b600082516112a281846020870161126c565b9190910192915050565b60208152600082518060208401526112cb81604085016020870161126c565b601f01601f1916919091016040019291505056fea264697066735822122000f918e3a59af92e2a14d9123b3fe1755ca0de77c365c4e3d6bd71bea319dedd64736f6c63430008120033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/SignMessageLib.json b/apps/api/src/app/hardhat/export/SignMessageLib.json new file mode 100644 index 000000000..1d87ebbc2 --- /dev/null +++ b/apps/api/src/app/hardhat/export/SignMessageLib.json @@ -0,0 +1,56 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "SignMessageLib", + "sourceName": "@gnosis.pm/safe-contracts/contracts/examples/libraries/SignMessage.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "SignMsg", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "getMessageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "signMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610392806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630a1028c41461003b57806385a5affe14610060575b600080fd5b61004e610049366004610220565b610075565b60405190815260200160405180910390f35b61007361006e3660046102d1565b610187565b005b6000807f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca60001b83805190602001206040516020016100be929190918252602082015260400190565b60408051601f19818403018152828252805160209182012063f698da2560e01b84529151919350601960f81b92600160f81b92309263f698da2592600480820193918290030181865afa158015610119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013d9190610343565b6040516001600160f81b0319938416602082015292909116602183015260228201526042810182905260620160405160208183030381529060405280519060200120915050919050565b60006101c883838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061007592505050565b600081815260076020526040808220600190555191925082917fe7f4675038f4f6034dfcbbb24c4dc08e4ebf10eb9d257d3d02c0f38d122ac6e49190a2505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561023257600080fd5b813567ffffffffffffffff8082111561024a57600080fd5b818401915084601f83011261025e57600080fd5b8135818111156102705761027061020a565b604051601f8201601f19908116603f011681019083821181831017156102985761029861020a565b816040528281528760208487010111156102b157600080fd5b826020860160208301376000928101602001929092525095945050505050565b600080602083850312156102e457600080fd5b823567ffffffffffffffff808211156102fc57600080fd5b818501915085601f83011261031057600080fd5b81358181111561031f57600080fd5b86602082850101111561033157600080fd5b60209290920196919550909350505050565b60006020828403121561035557600080fd5b505191905056fea2646970667358221220bc635c25890be8657513d1b90bb9807ae47e762473d8e970222944459b865a4e64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80630a1028c41461003b57806385a5affe14610060575b600080fd5b61004e610049366004610220565b610075565b60405190815260200160405180910390f35b61007361006e3660046102d1565b610187565b005b6000807f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca60001b83805190602001206040516020016100be929190918252602082015260400190565b60408051601f19818403018152828252805160209182012063f698da2560e01b84529151919350601960f81b92600160f81b92309263f698da2592600480820193918290030181865afa158015610119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013d9190610343565b6040516001600160f81b0319938416602082015292909116602183015260228201526042810182905260620160405160208183030381529060405280519060200120915050919050565b60006101c883838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061007592505050565b600081815260076020526040808220600190555191925082917fe7f4675038f4f6034dfcbbb24c4dc08e4ebf10eb9d257d3d02c0f38d122ac6e49190a2505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561023257600080fd5b813567ffffffffffffffff8082111561024a57600080fd5b818401915084601f83011261025e57600080fd5b8135818111156102705761027061020a565b604051601f8201601f19908116603f011681019083821181831017156102985761029861020a565b816040528281528760208487010111156102b157600080fd5b826020860160208301376000928101602001929092525095945050505050565b600080602083850312156102e457600080fd5b823567ffffffffffffffff808211156102fc57600080fd5b818501915085601f83011261031057600080fd5b81358181111561031f57600080fd5b86602082850101111561033157600080fd5b60209290920196919550909350505050565b60006020828403121561035557600080fd5b505191905056fea2646970667358221220bc635c25890be8657513d1b90bb9807ae47e762473d8e970222944459b865a4e64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/SimulateTxAccessor.json b/apps/api/src/app/hardhat/export/SimulateTxAccessor.json new file mode 100644 index 000000000..66961a800 --- /dev/null +++ b/apps/api/src/app/hardhat/export/SimulateTxAccessor.json @@ -0,0 +1,60 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "SimulateTxAccessor", + "sourceName": "@gnosis.pm/safe-contracts/contracts/accessors/SimulateTxAccessor.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "enum Enum.Operation", + "name": "operation", + "type": "uint8" + } + ], + "name": "simulate", + "outputs": [ + { + "internalType": "uint256", + "name": "estimate", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60a060405234801561001057600080fd5b503060805260805161035a61002f6000396000606a015261035a6000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80631c5fb21114610030575b600080fd5b61004361003e3660046101db565b61005b565b60405161005293929190610287565b60405180910390f35b60008060606001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036101025760405162461bcd60e51b815260206004820152603960248201527f53696d756c61746554784163636573736f722073686f756c64206f6e6c79206260448201527f652063616c6c6564207669612064656c656761746563616c6c00000000000000606482015260840160405180910390fd5b60005a905061014a898989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b925050505a610180565b92505a61015790826102e7565b935060405160203d0181016040523d81523d6000602083013e8092505050955095509592505050565b600060018360018111156101965761019661030e565b036101ae576000808551602087018986f490506101be565b600080855160208701888a87f190505b95945050505050565b8035600281106101d657600080fd5b919050565b6000806000806000608086880312156101f357600080fd5b85356001600160a01b038116811461020a57600080fd5b945060208601359350604086013567ffffffffffffffff8082111561022e57600080fd5b818801915088601f83011261024257600080fd5b81358181111561025157600080fd5b89602082850101111561026357600080fd5b60208301955080945050505061027b606087016101c7565b90509295509295909350565b83815260006020841515602084015260606040840152835180606085015260005b818110156102c4578581018301518582016080015282016102a8565b506000608082860101526080601f19601f83011685010192505050949350505050565b8181038181111561030857634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220bc1920ffea8ed24569e272631877ba36cb446b85161f4fe73e4d43555cbd51ac64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80631c5fb21114610030575b600080fd5b61004361003e3660046101db565b61005b565b60405161005293929190610287565b60405180910390f35b60008060606001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036101025760405162461bcd60e51b815260206004820152603960248201527f53696d756c61746554784163636573736f722073686f756c64206f6e6c79206260448201527f652063616c6c6564207669612064656c656761746563616c6c00000000000000606482015260840160405180910390fd5b60005a905061014a898989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b925050505a610180565b92505a61015790826102e7565b935060405160203d0181016040523d81523d6000602083013e8092505050955095509592505050565b600060018360018111156101965761019661030e565b036101ae576000808551602087018986f490506101be565b600080855160208701888a87f190505b95945050505050565b8035600281106101d657600080fd5b919050565b6000806000806000608086880312156101f357600080fd5b85356001600160a01b038116811461020a57600080fd5b945060208601359350604086013567ffffffffffffffff8082111561022e57600080fd5b818801915088601f83011261024257600080fd5b81358181111561025157600080fd5b89602082850101111561026357600080fd5b60208301955080945050505061027b606087016101c7565b90509295509295909350565b83815260006020841515602084015260606040840152835180606085015260005b818110156102c4578581018301518582016080015282016102a8565b506000608082860101526080601f19601f83011685010192505050949350505050565b8181038181111561030857634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220bc1920ffea8ed24569e272631877ba36cb446b85161f4fe73e4d43555cbd51ac64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/SmartWalletWhitelist.json b/apps/api/src/app/hardhat/export/SmartWalletWhitelist.json new file mode 100644 index 000000000..f6508b8dd --- /dev/null +++ b/apps/api/src/app/hardhat/export/SmartWalletWhitelist.json @@ -0,0 +1,229 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "SmartWalletWhitelist", + "sourceName": "contracts/utils/SmartWalletWhitelist.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "ApproveWallet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "NewChecker", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "RevokeWallet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "name": "SetAllowAll", + "type": "event" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wallet", + "type": "address" + } + ], + "name": "approveWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_wallets", + "type": "address[]" + } + ], + "name": "approveWalletList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wallet", + "type": "address" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checker", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isAllowAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wallet", + "type": "address" + } + ], + "name": "revokeWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_wallets", + "type": "address[]" + } + ], + "name": "revokeWalletList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_isAllowAll", + "type": "bool" + } + ], + "name": "setAllowAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_checker", + "type": "address" + } + ], + "name": "setChecker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "wallets", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60a06040526001805460ff60a01b1916905534801561001d57600080fd5b506040516107ed3803806107ed83398101604081905261003c9161004d565b6001600160a01b031660805261007d565b60006020828403121561005f57600080fd5b81516001600160a01b038116811461007657600080fd5b9392505050565b6080516107326100bb600039600081816101b1015281816101d50152818161030101528181610396015281816103e7015261054901526107326000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063808a9d4011610071578063808a9d401461012557806389b08f1114610138578063c23697a81461015b578063cf5303cf1461016e578063cf880f4c14610199578063f851a440146101ac57600080fd5b80630fcb0ae5146100ae5780631b833791146100c35780632c6766c6146100d6578063396ad9d3146100ff5780634d7d9c0114610112575b600080fd5b6100c16100bc3660046105dd565b6101d3565b005b6100c16100d1366004610606565b61027d565b6001546100ea90600160a01b900460ff1681565b60405190151581526020015b60405180910390f35b6100c161010d366004610606565b6102c1565b6100c161012036600461068c565b6102ff565b6100c16101333660046105dd565b610394565b6100ea6101463660046105dd565b60006020819052908152604090205460ff1681565b6100ea6101693660046105dd565b610473565b600154610181906001600160a01b031681565b6040516001600160a01b0390911681526020016100f6565b6100c16101a73660046105dd565b610547565b6101817f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146102245760405162461bcd60e51b815260040161021b906106a9565b60405180910390fd5b6001600160a01b03811660008181526020818152604091829020805460ff1916600117905590519182527fc1e7aae3f3125e58cfc69ab2a872a655dbb9427614aa85b29bb5abeaca4d6a9291015b60405180910390a150565b8060005b818110156102bb576102b384848381811061029e5761029e6106c9565b905060200201602081019061013391906105dd565b600101610281565b50505050565b8060005b818110156102bb576102f78484838181106102e2576102e26106c9565b90506020020160208101906100bc91906105dd565b6001016102c5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146103475760405162461bcd60e51b815260040161021b906106a9565b60018054821515600160a01b0260ff60a01b199091161790556040517fd6b910c3c867dfdca6ffe654abf208c60c6b831d36ddc04b3c5f43c2982caa379061027290831515815260200190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146103dc5760405162461bcd60e51b815260040161021b906106a9565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104245760405162461bcd60e51b815260040161021b906106a9565b6001600160a01b03811660008181526020818152604091829020805460ff1916905590519182527f1b676c3cc753786cb95aff57280fd7406f1da74e2a8b9755fdd395aded3e16dd9101610272565b600154600090600160a01b900460ff161561049057506001919050565b6001600160a01b03821660009081526020819052604090205460ff1680156104b85792915050565b6001546001600160a01b03161561053e57600154604051631846d2f560e31b81526001600160a01b0385811660048301529091169063c23697a890602401602060405180830381865afa158015610513573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053791906106df565b9392505050565b50600092915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316331461058f5760405162461bcd60e51b815260040161021b906106a9565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527fbbb218c618aff8fc737012ba779aad87c1cf7a60b501bd2f72bb570517f02ca390602001610272565b6000602082840312156105ef57600080fd5b81356001600160a01b038116811461053757600080fd5b6000806020838503121561061957600080fd5b823567ffffffffffffffff8082111561063157600080fd5b818501915085601f83011261064557600080fd5b81358181111561065457600080fd5b8660208260051b850101111561066957600080fd5b60209290920196919550909350505050565b801515811461068957600080fd5b50565b60006020828403121561069e57600080fd5b81356105378161067b565b60208082526006908201526510b0b236b4b760d11b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156106f157600080fd5b81516105378161067b56fea26469706673582212205ce205a4575174bf41d715179b300702d85eae262bc2732b0cb6d1c8c6358bcc64736f6c63430008120033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063808a9d4011610071578063808a9d401461012557806389b08f1114610138578063c23697a81461015b578063cf5303cf1461016e578063cf880f4c14610199578063f851a440146101ac57600080fd5b80630fcb0ae5146100ae5780631b833791146100c35780632c6766c6146100d6578063396ad9d3146100ff5780634d7d9c0114610112575b600080fd5b6100c16100bc3660046105dd565b6101d3565b005b6100c16100d1366004610606565b61027d565b6001546100ea90600160a01b900460ff1681565b60405190151581526020015b60405180910390f35b6100c161010d366004610606565b6102c1565b6100c161012036600461068c565b6102ff565b6100c16101333660046105dd565b610394565b6100ea6101463660046105dd565b60006020819052908152604090205460ff1681565b6100ea6101693660046105dd565b610473565b600154610181906001600160a01b031681565b6040516001600160a01b0390911681526020016100f6565b6100c16101a73660046105dd565b610547565b6101817f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146102245760405162461bcd60e51b815260040161021b906106a9565b60405180910390fd5b6001600160a01b03811660008181526020818152604091829020805460ff1916600117905590519182527fc1e7aae3f3125e58cfc69ab2a872a655dbb9427614aa85b29bb5abeaca4d6a9291015b60405180910390a150565b8060005b818110156102bb576102b384848381811061029e5761029e6106c9565b905060200201602081019061013391906105dd565b600101610281565b50505050565b8060005b818110156102bb576102f78484838181106102e2576102e26106c9565b90506020020160208101906100bc91906105dd565b6001016102c5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146103475760405162461bcd60e51b815260040161021b906106a9565b60018054821515600160a01b0260ff60a01b199091161790556040517fd6b910c3c867dfdca6ffe654abf208c60c6b831d36ddc04b3c5f43c2982caa379061027290831515815260200190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146103dc5760405162461bcd60e51b815260040161021b906106a9565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104245760405162461bcd60e51b815260040161021b906106a9565b6001600160a01b03811660008181526020818152604091829020805460ff1916905590519182527f1b676c3cc753786cb95aff57280fd7406f1da74e2a8b9755fdd395aded3e16dd9101610272565b600154600090600160a01b900460ff161561049057506001919050565b6001600160a01b03821660009081526020819052604090205460ff1680156104b85792915050565b6001546001600160a01b03161561053e57600154604051631846d2f560e31b81526001600160a01b0385811660048301529091169063c23697a890602401602060405180830381865afa158015610513573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053791906106df565b9392505050565b50600092915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316331461058f5760405162461bcd60e51b815260040161021b906106a9565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527fbbb218c618aff8fc737012ba779aad87c1cf7a60b501bd2f72bb570517f02ca390602001610272565b6000602082840312156105ef57600080fd5b81356001600160a01b038116811461053757600080fd5b6000806020838503121561061957600080fd5b823567ffffffffffffffff8082111561063157600080fd5b818501915085601f83011261064557600080fd5b81358181111561065457600080fd5b8660208260051b850101111561066957600080fd5b60209290920196919550909350505050565b801515811461068957600080fd5b50565b60006020828403121561069e57600080fd5b81356105378161067b565b60208082526006908201526510b0b236b4b760d11b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156106f157600080fd5b81516105378161067b56fea26469706673582212205ce205a4575174bf41d715179b300702d85eae262bc2732b0cb6d1c8c6358bcc64736f6c63430008120033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THX.json b/apps/api/src/app/hardhat/export/THX.json new file mode 100644 index 000000000..56d838db6 --- /dev/null +++ b/apps/api/src/app/hardhat/export/THX.json @@ -0,0 +1,297 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THX", + "sourceName": "contracts/mock/THX.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000cf738038062000cf7833981810160405260408110156200003757600080fd5b5080516020918201516040805180820182526011815270544858204e6574776f726b2028506f532960781b81860190815282518084019093526003808452620a890b60eb1b968401969096528151949593949193620000999290919062000249565b508051620000af90600490602084019062000249565b50506005805460ff1916601217905550620000cb8282620000d3565b5050620002f5565b6001600160a01b0382166200012f576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6200013d60008383620001e2565b6200015981600254620001e760201b620005731790919060201c565b6002556001600160a01b038216600090815260208181526040909120546200018c91839062000573620001e7821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b60008282018381101562000242576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002815760008555620002cc565b82601f106200029c57805160ff1916838001178555620002cc565b82800160010185558215620002cc579182015b82811115620002cc578251825591602001919060010190620002af565b50620002da929150620002de565b5090565b5b80821115620002da5760008155600101620002df565b6109f280620003056000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103f9565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610402565b6101736004803603602081101561021b57600080fd5b50356001600160a01b0316610450565b6100b661046b565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104cc565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610534565b610173600480360360408110156102a157600080fd5b506001600160a01b0381358116916020013516610548565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c6105d4565b84846105d8565b50600192915050565b60025490565b600061037f8484846106c4565b6103ef8461038b6105d4565b6103ea85604051806060016040528060288152602001610927602891396001600160a01b038a166000908152600160205260408120906103c96105d4565b6001600160a01b03168152602081019190915260400160002054919061081f565b6105d8565b5060019392505050565b60055460ff1690565b600061036361040f6105d4565b846103ea85600160006104206105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610573565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104d96105d4565b846103ea8560405180606001604052806025815260200161099860259139600160006105036105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061081f565b60006103636105416105d4565b84846106c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6000828201838110156105cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b03831661061d5760405162461bcd60e51b81526004018080602001828103825260248152602001806109746024913960400191505060405180910390fd5b6001600160a01b0382166106625760405162461bcd60e51b81526004018080602001828103825260228152602001806108df6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107095760405162461bcd60e51b815260040180806020018281038252602581526020018061094f6025913960400191505060405180910390fd5b6001600160a01b03821661074e5760405162461bcd60e51b81526004018080602001828103825260238152602001806108bc6023913960400191505060405180910390fd5b6107598383836108b6565b61079681604051806060016040528060268152602001610901602691396001600160a01b038616600090815260208190526040902054919061081f565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c59082610573565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108ae5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087357818101518382015260200161085b565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122077c110b83655fc51968e38fdc606b94ac5819151c022a8cf8ebfabe88559128464736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103f9565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610402565b6101736004803603602081101561021b57600080fd5b50356001600160a01b0316610450565b6100b661046b565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104cc565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610534565b610173600480360360408110156102a157600080fd5b506001600160a01b0381358116916020013516610548565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c6105d4565b84846105d8565b50600192915050565b60025490565b600061037f8484846106c4565b6103ef8461038b6105d4565b6103ea85604051806060016040528060288152602001610927602891396001600160a01b038a166000908152600160205260408120906103c96105d4565b6001600160a01b03168152602081019190915260400160002054919061081f565b6105d8565b5060019392505050565b60055460ff1690565b600061036361040f6105d4565b846103ea85600160006104206105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610573565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104d96105d4565b846103ea8560405180606001604052806025815260200161099860259139600160006105036105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061081f565b60006103636105416105d4565b84846106c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6000828201838110156105cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b03831661061d5760405162461bcd60e51b81526004018080602001828103825260248152602001806109746024913960400191505060405180910390fd5b6001600160a01b0382166106625760405162461bcd60e51b81526004018080602001828103825260228152602001806108df6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107095760405162461bcd60e51b815260040180806020018281038252602581526020018061094f6025913960400191505060405180910390fd5b6001600160a01b03821661074e5760405162461bcd60e51b81526004018080602001828103825260238152602001806108bc6023913960400191505060405180910390fd5b6107598383836108b6565b61079681604051806060016040528060268152602001610901602691396001600160a01b038616600090815260208190526040902054919061081f565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c59082610573565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108ae5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087357818101518382015260200161085b565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122077c110b83655fc51968e38fdc606b94ac5819151c022a8cf8ebfabe88559128464736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THXERC1155.json b/apps/api/src/app/hardhat/export/THXERC1155.json new file mode 100644 index 000000000..614ddf887 --- /dev/null +++ b/apps/api/src/app/hardhat/export/THXERC1155.json @@ -0,0 +1,688 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THX_ERC1155", + "sourceName": "contracts/utils/ERC1155/THX_ERC1155.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "URI_", + "type": "string" + }, + { + "internalType": "address", + "name": "owner_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINTER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mintBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162002b6838038062002b68833981810160405260408110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b50604052602001519150829050620001056301ffc9a760e01b620001ca565b62000110816200024f565b62000122636cdb3d1360e11b620001ca565b620001346303a24d0760e21b620001ca565b5060006200014162000268565b600580546001600160a01b0319166001600160a01b0383169081179091556040519192509060009060008051602062002b48833981519152908290a35062000189816200026c565b6200019660008262000377565b620001c27f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68262000377565b505062000540565b6001600160e01b031980821614156200022a576040805162461bcd60e51b815260206004820152601c60248201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604482015290519081900360640190fd5b6001600160e01b0319166000908152602081905260409020805460ff19166001179055565b80516200026490600390602084019062000494565b5050565b3390565b6200027662000268565b6001600160a01b03166200028962000383565b6001600160a01b031614620002e5576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166200032c5760405162461bcd60e51b815260040180806020018281038252602681526020018062002b226026913960400191505060405180910390fd5b6005546040516001600160a01b0380841692169060008051602062002b4883398151915290600090a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b62000264828262000392565b6005546001600160a01b031690565b6000828152600460209081526040909120620003b9918390620017116200040d821b17901c565b156200026457620003c962000268565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600062000424836001600160a01b0384166200042d565b90505b92915050565b60006200043b83836200047c565b620004735750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000427565b50600062000427565b60009081526001919091016020526040902054151590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620004cc576000855562000517565b82601f10620004e757805160ff191683800117855562000517565b8280016001018555821562000517579182015b8281111562000517578251825591602001919060010190620004fa565b506200052592915062000529565b5090565b5b808211156200052557600081556001016200052a565b6125d280620005506000396000f3fe608060405234801561001057600080fd5b50600436106101415760003560e01c80638da5cb5b116100b8578063ca15c8731161007c578063ca15c87314610925578063d539139314610942578063d547741f1461094a578063e985e9c514610976578063f242432a146109a4578063f2fde38b14610a6d57610141565b80638da5cb5b1461087c5780639010d07c146108a057806391d14854146108c3578063a217fddf146108ef578063a22cb465146108f757610141565b80632eb2c2d61161010a5780632eb2c2d6146104285780632f2ff15d146105e957806336568abe146106155780634e1273f414610641578063715018a6146107b4578063731133e9146107bc57610141565b8062fdd58e1461014657806301ffc9a7146101845780630e89341c146101bf5780631f7fdffa14610251578063248a9ca31461040b575b600080fd5b6101726004803603604081101561015c57600080fd5b506001600160a01b038135169060200135610a93565b60408051918252519081900360200190f35b6101ab6004803603602081101561019a57600080fd5b50356001600160e01b031916610b05565b604080519115158252519081900360200190f35b6101dc600480360360208110156101d557600080fd5b5035610b24565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102165781810151838201526020016101fe565b50505050905090810190601f1680156102435780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6104096004803603608081101561026757600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561029157600080fd5b8201836020820111156102a357600080fd5b803590602001918460208302840111600160201b831117156102c457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561031357600080fd5b82018360208201111561032557600080fd5b803590602001918460208302840111600160201b8311171561034657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561039557600080fd5b8201836020820111156103a757600080fd5b803590602001918460018302840111600160201b831117156103c857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610bbc945050505050565b005b6101726004803603602081101561042157600080fd5b5035610c36565b610409600480360360a081101561043e57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561047157600080fd5b82018360208201111561048357600080fd5b803590602001918460208302840111600160201b831117156104a457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156104f357600080fd5b82018360208201111561050557600080fd5b803590602001918460208302840111600160201b8311171561052657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561057557600080fd5b82018360208201111561058757600080fd5b803590602001918460018302840111600160201b831117156105a857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610c4b945050505050565b610409600480360360408110156105ff57600080fd5b50803590602001356001600160a01b0316610f49565b6104096004803603604081101561062b57600080fd5b50803590602001356001600160a01b0316610fb5565b6107646004803603604081101561065757600080fd5b810190602081018135600160201b81111561067157600080fd5b82018360208201111561068357600080fd5b803590602001918460208302840111600160201b831117156106a457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156106f357600080fd5b82018360208201111561070557600080fd5b803590602001918460208302840111600160201b8311171561072657600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611016945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156107a0578181015183820152602001610788565b505050509050019250505060405180910390f35b610409611102565b610409600480360360808110156107d257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b81111561080857600080fd5b82018360208201111561081a57600080fd5b803590602001918460018302840111600160201b8311171561083b57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506111c0945050505050565b610884611234565b604080516001600160a01b039092168252519081900360200190f35b610884600480360360408110156108b657600080fd5b5080359060200135611244565b6101ab600480360360408110156108d957600080fd5b50803590602001356001600160a01b0316611263565b61017261127b565b6104096004803603604081101561090d57600080fd5b506001600160a01b0381351690602001351515611280565b6101726004803603602081101561093b57600080fd5b503561136f565b610172611386565b6104096004803603604081101561096057600080fd5b50803590602001356001600160a01b03166113aa565b6101ab6004803603604081101561098c57600080fd5b506001600160a01b0381358116916020013516611403565b610409600480360360a08110156109ba57600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156109f957600080fd5b820183602082011115610a0b57600080fd5b803590602001918460018302840111600160201b83111715610a2c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611431945050505050565b61040960048036036020811015610a8357600080fd5b50356001600160a01b03166115fc565b60006001600160a01b038316610ada5760405162461bcd60e51b815260040180806020018281038252602b8152602001806123a8602b913960400191505060405180910390fd5b5060008181526001602090815260408083206001600160a01b03861684529091529020545b92915050565b6001600160e01b03191660009081526020819052604090205460ff1690565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610bb05780601f10610b8557610100808354040283529160200191610bb0565b820191906000526020600020905b815481529060010190602001808311610b9357829003601f168201915b50505050509050919050565b610be67f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633611263565b610c24576040805162461bcd60e51b815260206004820152600a6024820152692727aa2fa6a4a72a22a960b11b604482015290519081900360640190fd5b610c3084848484611726565b50505050565b60009081526004602052604090206002015490565b8151835114610c8b5760405162461bcd60e51b81526004018080602001828103825260288152602001806125256028913960400191505060405180910390fd5b6001600160a01b038416610cd05760405162461bcd60e51b81526004018080602001828103825260258152602001806124526025913960400191505060405180910390fd5b610cd861197b565b6001600160a01b0316856001600160a01b03161480610d035750610d0385610cfe61197b565b611403565b610d3e5760405162461bcd60e51b81526004018080602001828103825260328152602001806124776032913960400191505060405180910390fd5b6000610d4861197b565b9050610d58818787878787610f41565b60005b8451811015610e59576000858281518110610d7257fe5b602002602001015190506000858381518110610d8a57fe5b60200260200101519050610df7816040518060600160405280602a81526020016124a9602a91396001600086815260200190815260200160002060008d6001600160a01b03166001600160a01b031681526020019081526020016000205461197f9092919063ffffffff16565b60008381526001602090815260408083206001600160a01b038e811685529252808320939093558a1681522054610e2e9082611a16565b60009283526001602081815260408086206001600160a01b038d168752909152909320555001610d5b565b50846001600160a01b0316866001600160a01b0316826001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610edf578181015183820152602001610ec7565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610f1e578181015183820152602001610f06565b5050505090500194505050505060405180910390a4610f41818787878787611a70565b505050505050565b600082815260046020526040902060020154610f6c90610f6761197b565b611263565b610fa75760405162461bcd60e51b815260040180806020018281038252602f815260200180612379602f913960400191505060405180910390fd5b610fb18282611cef565b5050565b610fbd61197b565b6001600160a01b0316816001600160a01b03161461100c5760405162461bcd60e51b815260040180806020018281038252602f81526020018061256e602f913960400191505060405180910390fd5b610fb18282611d58565b606081518351146110585760405162461bcd60e51b81526004018080602001828103825260298152602001806124fc6029913960400191505060405180910390fd5b6000835167ffffffffffffffff8111801561107257600080fd5b5060405190808252806020026020018201604052801561109c578160200160208202803683370190505b50905060005b84518110156110fa576110db8582815181106110ba57fe5b60200260200101518583815181106110ce57fe5b6020026020010151610a93565b8282815181106110e757fe5b60209081029190910101526001016110a2565b509392505050565b61110a61197b565b6001600160a01b031661111b611234565b6001600160a01b031614611176576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6005546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600580546001600160a01b0319169055565b6111ea7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633611263565b611228576040805162461bcd60e51b815260206004820152600a6024820152692727aa2fa6a4a72a22a960b11b604482015290519081900360640190fd5b610c3084848484611dc1565b6005546001600160a01b03165b90565b600082815260046020526040812061125c9083611ec2565b9392505050565b600082815260046020526040812061125c9083611ece565b600081565b816001600160a01b031661129261197b565b6001600160a01b031614156112d85760405162461bcd60e51b81526004018080602001828103825260298152602001806124d36029913960400191505060405180910390fd5b80600260006112e561197b565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff19169215159290921790915561132961197b565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c318360405180821515815260200191505060405180910390a35050565b6000818152600460205260408120610aff90611ee3565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6000828152600460205260409020600201546113c890610f6761197b565b61100c5760405162461bcd60e51b81526004018080602001828103825260308152602001806124226030913960400191505060405180910390fd5b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b6001600160a01b0384166114765760405162461bcd60e51b81526004018080602001828103825260258152602001806124526025913960400191505060405180910390fd5b61147e61197b565b6001600160a01b0316856001600160a01b031614806114a457506114a485610cfe61197b565b6114df5760405162461bcd60e51b81526004018080602001828103825260298152602001806123f96029913960400191505060405180910390fd5b60006114e961197b565b90506115098187876114fa88611eee565b61150388611eee565b87610f41565b611550836040518060600160405280602a81526020016124a9602a913960008781526001602090815260408083206001600160a01b038d168452909152902054919061197f565b60008581526001602090815260408083206001600160a01b038b811685529252808320939093558716815220546115879084611a16565b60008581526001602090815260408083206001600160a01b03808b168086529184529382902094909455805188815291820187905280518a8416938616927fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6292908290030190a4610f41818787878787611f33565b61160461197b565b6001600160a01b0316611615611234565b6001600160a01b031614611670576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166116b55760405162461bcd60e51b81526004018080602001828103825260268152602001806123d36026913960400191505060405180910390fd5b6005546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b600061125c836001600160a01b0384166120a4565b6001600160a01b03841661176b5760405162461bcd60e51b815260040180806020018281038252602181526020018061254d6021913960400191505060405180910390fd5b81518351146117ab5760405162461bcd60e51b81526004018080602001828103825260288152602001806125256028913960400191505060405180910390fd5b60006117b561197b565b90506117c681600087878787610f41565b60005b845181101561188a57611841600160008784815181106117e557fe5b602002602001015181526020019081526020016000206000886001600160a01b03166001600160a01b031681526020019081526020016000205485838151811061182b57fe5b6020026020010151611a1690919063ffffffff16565b6001600087848151811061185157fe5b602090810291909101810151825281810192909252604090810160009081206001600160a01b038b1682529092529020556001016117c9565b50846001600160a01b031660006001600160a01b0316826001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156119115781810151838201526020016118f9565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015611950578181015183820152602001611938565b5050505090500194505050505060405180910390a461197481600087878787611a70565b5050505050565b3390565b60008184841115611a0e5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156119d35781810151838201526020016119bb565b50505050905090810190601f168015611a005780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60008282018381101561125c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b611a82846001600160a01b03166120ee565b15610f4157836001600160a01b031663bc197c8187878686866040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015611b10578181015183820152602001611af8565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015611b4f578181015183820152602001611b37565b50505050905001848103825285818151815260200191508051906020019080838360005b83811015611b8b578181015183820152602001611b73565b50505050905090810190601f168015611bb85780820380516001836020036101000a031916815260200191505b5098505050505050505050602060405180830381600087803b158015611bdd57600080fd5b505af1925050508015611c0257506040513d6020811015611bfd57600080fd5b505160015b611c9757611c0e612255565b80611c195750611c60565b60405162461bcd60e51b81526020600482018181528351602484015283518493919283926044019190850190808383600083156119d35781810151838201526020016119bb565b60405162461bcd60e51b81526004018080602001828103825260348152602001806122fb6034913960400191505060405180910390fd5b6001600160e01b0319811663bc197c8160e01b14611ce65760405162461bcd60e51b81526004018080602001828103825260288152602001806123516028913960400191505060405180910390fd5b50505050505050565b6000828152600460205260409020611d079082611711565b15610fb157611d1461197b565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152600460205260409020611d7090826120f4565b15610fb157611d7d61197b565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6001600160a01b038416611e065760405162461bcd60e51b815260040180806020018281038252602181526020018061254d6021913960400191505060405180910390fd5b6000611e1061197b565b9050611e22816000876114fa88611eee565b60008481526001602090815260408083206001600160a01b0389168452909152902054611e4f9084611a16565b60008581526001602090815260408083206001600160a01b03808b16808652918452828520959095558151898152928301889052815190948616927fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6292908290030190a461197481600087878787611f33565b600061125c8383612109565b600061125c836001600160a01b03841661216d565b6000610aff82612185565b60408051600180825281830190925260609160009190602080830190803683370190505090508281600081518110611f2257fe5b602090810291909101015292915050565b611f45846001600160a01b03166120ee565b15610f4157836001600160a01b031663f23a6e6187878686866040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b0316815260200184815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611fd4578181015183820152602001611fbc565b50505050905090810190601f1680156120015780820380516001836020036101000a031916815260200191505b509650505050505050602060405180830381600087803b15801561202457600080fd5b505af192505050801561204957506040513d602081101561204457600080fd5b505160015b61205557611c0e612255565b6001600160e01b0319811663f23a6e6160e01b14611ce65760405162461bcd60e51b81526004018080602001828103825260288152602001806123516028913960400191505060405180910390fd5b60006120b0838361216d565b6120e657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aff565b506000610aff565b3b151590565b600061125c836001600160a01b038416612189565b8154600090821061214b5760405162461bcd60e51b815260040180806020018281038252602281526020018061232f6022913960400191505060405180910390fd5b82600001828154811061215a57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b6000818152600183016020526040812054801561224557835460001980830191908101906000908790839081106121bc57fe5b90600052602060002001549050808760000184815481106121d957fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061220957fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610aff565b6000915050610aff565b60e01c90565b600060443d101561226557611241565b600481823e6308c379a0612279825161224f565b1461228357611241565b6040513d600319016004823e80513d67ffffffffffffffff81602484011181841117156122b35750505050611241565b828401925082519150808211156122cd5750505050611241565b503d830160208284010111156122e557505050611241565b601f01601f191681016020016040529150509056fe455243313135353a207472616e7366657220746f206e6f6e2045524331313535526563656976657220696d706c656d656e746572456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473455243313135353a204552433131353552656365697665722072656a656374656420746f6b656e73416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74455243313135353a2062616c616e636520717565727920666f7220746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373455243313135353a2063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65455243313135353a207472616e7366657220746f20746865207a65726f2061646472657373455243313135353a207472616e736665722063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564455243313135353a20696e73756666696369656e742062616c616e636520666f72207472616e73666572455243313135353a2073657474696e6720617070726f76616c2073746174757320666f722073656c66455243313135353a206163636f756e747320616e6420696473206c656e677468206d69736d61746368455243313135353a2069647320616e6420616d6f756e7473206c656e677468206d69736d61746368455243313135353a206d696e7420746f20746865207a65726f2061646472657373416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a26469706673582212206b93266a963cefb5dcc7161758be463edca28a8dc8337e55beefbbabc25f14ee64736f6c634300070600334f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573738be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101415760003560e01c80638da5cb5b116100b8578063ca15c8731161007c578063ca15c87314610925578063d539139314610942578063d547741f1461094a578063e985e9c514610976578063f242432a146109a4578063f2fde38b14610a6d57610141565b80638da5cb5b1461087c5780639010d07c146108a057806391d14854146108c3578063a217fddf146108ef578063a22cb465146108f757610141565b80632eb2c2d61161010a5780632eb2c2d6146104285780632f2ff15d146105e957806336568abe146106155780634e1273f414610641578063715018a6146107b4578063731133e9146107bc57610141565b8062fdd58e1461014657806301ffc9a7146101845780630e89341c146101bf5780631f7fdffa14610251578063248a9ca31461040b575b600080fd5b6101726004803603604081101561015c57600080fd5b506001600160a01b038135169060200135610a93565b60408051918252519081900360200190f35b6101ab6004803603602081101561019a57600080fd5b50356001600160e01b031916610b05565b604080519115158252519081900360200190f35b6101dc600480360360208110156101d557600080fd5b5035610b24565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102165781810151838201526020016101fe565b50505050905090810190601f1680156102435780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6104096004803603608081101561026757600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561029157600080fd5b8201836020820111156102a357600080fd5b803590602001918460208302840111600160201b831117156102c457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561031357600080fd5b82018360208201111561032557600080fd5b803590602001918460208302840111600160201b8311171561034657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561039557600080fd5b8201836020820111156103a757600080fd5b803590602001918460018302840111600160201b831117156103c857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610bbc945050505050565b005b6101726004803603602081101561042157600080fd5b5035610c36565b610409600480360360a081101561043e57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561047157600080fd5b82018360208201111561048357600080fd5b803590602001918460208302840111600160201b831117156104a457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156104f357600080fd5b82018360208201111561050557600080fd5b803590602001918460208302840111600160201b8311171561052657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561057557600080fd5b82018360208201111561058757600080fd5b803590602001918460018302840111600160201b831117156105a857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610c4b945050505050565b610409600480360360408110156105ff57600080fd5b50803590602001356001600160a01b0316610f49565b6104096004803603604081101561062b57600080fd5b50803590602001356001600160a01b0316610fb5565b6107646004803603604081101561065757600080fd5b810190602081018135600160201b81111561067157600080fd5b82018360208201111561068357600080fd5b803590602001918460208302840111600160201b831117156106a457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156106f357600080fd5b82018360208201111561070557600080fd5b803590602001918460208302840111600160201b8311171561072657600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611016945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156107a0578181015183820152602001610788565b505050509050019250505060405180910390f35b610409611102565b610409600480360360808110156107d257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b81111561080857600080fd5b82018360208201111561081a57600080fd5b803590602001918460018302840111600160201b8311171561083b57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506111c0945050505050565b610884611234565b604080516001600160a01b039092168252519081900360200190f35b610884600480360360408110156108b657600080fd5b5080359060200135611244565b6101ab600480360360408110156108d957600080fd5b50803590602001356001600160a01b0316611263565b61017261127b565b6104096004803603604081101561090d57600080fd5b506001600160a01b0381351690602001351515611280565b6101726004803603602081101561093b57600080fd5b503561136f565b610172611386565b6104096004803603604081101561096057600080fd5b50803590602001356001600160a01b03166113aa565b6101ab6004803603604081101561098c57600080fd5b506001600160a01b0381358116916020013516611403565b610409600480360360a08110156109ba57600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156109f957600080fd5b820183602082011115610a0b57600080fd5b803590602001918460018302840111600160201b83111715610a2c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611431945050505050565b61040960048036036020811015610a8357600080fd5b50356001600160a01b03166115fc565b60006001600160a01b038316610ada5760405162461bcd60e51b815260040180806020018281038252602b8152602001806123a8602b913960400191505060405180910390fd5b5060008181526001602090815260408083206001600160a01b03861684529091529020545b92915050565b6001600160e01b03191660009081526020819052604090205460ff1690565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610bb05780601f10610b8557610100808354040283529160200191610bb0565b820191906000526020600020905b815481529060010190602001808311610b9357829003601f168201915b50505050509050919050565b610be67f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633611263565b610c24576040805162461bcd60e51b815260206004820152600a6024820152692727aa2fa6a4a72a22a960b11b604482015290519081900360640190fd5b610c3084848484611726565b50505050565b60009081526004602052604090206002015490565b8151835114610c8b5760405162461bcd60e51b81526004018080602001828103825260288152602001806125256028913960400191505060405180910390fd5b6001600160a01b038416610cd05760405162461bcd60e51b81526004018080602001828103825260258152602001806124526025913960400191505060405180910390fd5b610cd861197b565b6001600160a01b0316856001600160a01b03161480610d035750610d0385610cfe61197b565b611403565b610d3e5760405162461bcd60e51b81526004018080602001828103825260328152602001806124776032913960400191505060405180910390fd5b6000610d4861197b565b9050610d58818787878787610f41565b60005b8451811015610e59576000858281518110610d7257fe5b602002602001015190506000858381518110610d8a57fe5b60200260200101519050610df7816040518060600160405280602a81526020016124a9602a91396001600086815260200190815260200160002060008d6001600160a01b03166001600160a01b031681526020019081526020016000205461197f9092919063ffffffff16565b60008381526001602090815260408083206001600160a01b038e811685529252808320939093558a1681522054610e2e9082611a16565b60009283526001602081815260408086206001600160a01b038d168752909152909320555001610d5b565b50846001600160a01b0316866001600160a01b0316826001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610edf578181015183820152602001610ec7565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610f1e578181015183820152602001610f06565b5050505090500194505050505060405180910390a4610f41818787878787611a70565b505050505050565b600082815260046020526040902060020154610f6c90610f6761197b565b611263565b610fa75760405162461bcd60e51b815260040180806020018281038252602f815260200180612379602f913960400191505060405180910390fd5b610fb18282611cef565b5050565b610fbd61197b565b6001600160a01b0316816001600160a01b03161461100c5760405162461bcd60e51b815260040180806020018281038252602f81526020018061256e602f913960400191505060405180910390fd5b610fb18282611d58565b606081518351146110585760405162461bcd60e51b81526004018080602001828103825260298152602001806124fc6029913960400191505060405180910390fd5b6000835167ffffffffffffffff8111801561107257600080fd5b5060405190808252806020026020018201604052801561109c578160200160208202803683370190505b50905060005b84518110156110fa576110db8582815181106110ba57fe5b60200260200101518583815181106110ce57fe5b6020026020010151610a93565b8282815181106110e757fe5b60209081029190910101526001016110a2565b509392505050565b61110a61197b565b6001600160a01b031661111b611234565b6001600160a01b031614611176576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6005546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600580546001600160a01b0319169055565b6111ea7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633611263565b611228576040805162461bcd60e51b815260206004820152600a6024820152692727aa2fa6a4a72a22a960b11b604482015290519081900360640190fd5b610c3084848484611dc1565b6005546001600160a01b03165b90565b600082815260046020526040812061125c9083611ec2565b9392505050565b600082815260046020526040812061125c9083611ece565b600081565b816001600160a01b031661129261197b565b6001600160a01b031614156112d85760405162461bcd60e51b81526004018080602001828103825260298152602001806124d36029913960400191505060405180910390fd5b80600260006112e561197b565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff19169215159290921790915561132961197b565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c318360405180821515815260200191505060405180910390a35050565b6000818152600460205260408120610aff90611ee3565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6000828152600460205260409020600201546113c890610f6761197b565b61100c5760405162461bcd60e51b81526004018080602001828103825260308152602001806124226030913960400191505060405180910390fd5b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b6001600160a01b0384166114765760405162461bcd60e51b81526004018080602001828103825260258152602001806124526025913960400191505060405180910390fd5b61147e61197b565b6001600160a01b0316856001600160a01b031614806114a457506114a485610cfe61197b565b6114df5760405162461bcd60e51b81526004018080602001828103825260298152602001806123f96029913960400191505060405180910390fd5b60006114e961197b565b90506115098187876114fa88611eee565b61150388611eee565b87610f41565b611550836040518060600160405280602a81526020016124a9602a913960008781526001602090815260408083206001600160a01b038d168452909152902054919061197f565b60008581526001602090815260408083206001600160a01b038b811685529252808320939093558716815220546115879084611a16565b60008581526001602090815260408083206001600160a01b03808b168086529184529382902094909455805188815291820187905280518a8416938616927fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6292908290030190a4610f41818787878787611f33565b61160461197b565b6001600160a01b0316611615611234565b6001600160a01b031614611670576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166116b55760405162461bcd60e51b81526004018080602001828103825260268152602001806123d36026913960400191505060405180910390fd5b6005546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b600061125c836001600160a01b0384166120a4565b6001600160a01b03841661176b5760405162461bcd60e51b815260040180806020018281038252602181526020018061254d6021913960400191505060405180910390fd5b81518351146117ab5760405162461bcd60e51b81526004018080602001828103825260288152602001806125256028913960400191505060405180910390fd5b60006117b561197b565b90506117c681600087878787610f41565b60005b845181101561188a57611841600160008784815181106117e557fe5b602002602001015181526020019081526020016000206000886001600160a01b03166001600160a01b031681526020019081526020016000205485838151811061182b57fe5b6020026020010151611a1690919063ffffffff16565b6001600087848151811061185157fe5b602090810291909101810151825281810192909252604090810160009081206001600160a01b038b1682529092529020556001016117c9565b50846001600160a01b031660006001600160a01b0316826001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156119115781810151838201526020016118f9565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015611950578181015183820152602001611938565b5050505090500194505050505060405180910390a461197481600087878787611a70565b5050505050565b3390565b60008184841115611a0e5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156119d35781810151838201526020016119bb565b50505050905090810190601f168015611a005780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60008282018381101561125c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b611a82846001600160a01b03166120ee565b15610f4157836001600160a01b031663bc197c8187878686866040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015611b10578181015183820152602001611af8565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015611b4f578181015183820152602001611b37565b50505050905001848103825285818151815260200191508051906020019080838360005b83811015611b8b578181015183820152602001611b73565b50505050905090810190601f168015611bb85780820380516001836020036101000a031916815260200191505b5098505050505050505050602060405180830381600087803b158015611bdd57600080fd5b505af1925050508015611c0257506040513d6020811015611bfd57600080fd5b505160015b611c9757611c0e612255565b80611c195750611c60565b60405162461bcd60e51b81526020600482018181528351602484015283518493919283926044019190850190808383600083156119d35781810151838201526020016119bb565b60405162461bcd60e51b81526004018080602001828103825260348152602001806122fb6034913960400191505060405180910390fd5b6001600160e01b0319811663bc197c8160e01b14611ce65760405162461bcd60e51b81526004018080602001828103825260288152602001806123516028913960400191505060405180910390fd5b50505050505050565b6000828152600460205260409020611d079082611711565b15610fb157611d1461197b565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152600460205260409020611d7090826120f4565b15610fb157611d7d61197b565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6001600160a01b038416611e065760405162461bcd60e51b815260040180806020018281038252602181526020018061254d6021913960400191505060405180910390fd5b6000611e1061197b565b9050611e22816000876114fa88611eee565b60008481526001602090815260408083206001600160a01b0389168452909152902054611e4f9084611a16565b60008581526001602090815260408083206001600160a01b03808b16808652918452828520959095558151898152928301889052815190948616927fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6292908290030190a461197481600087878787611f33565b600061125c8383612109565b600061125c836001600160a01b03841661216d565b6000610aff82612185565b60408051600180825281830190925260609160009190602080830190803683370190505090508281600081518110611f2257fe5b602090810291909101015292915050565b611f45846001600160a01b03166120ee565b15610f4157836001600160a01b031663f23a6e6187878686866040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b0316815260200184815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611fd4578181015183820152602001611fbc565b50505050905090810190601f1680156120015780820380516001836020036101000a031916815260200191505b509650505050505050602060405180830381600087803b15801561202457600080fd5b505af192505050801561204957506040513d602081101561204457600080fd5b505160015b61205557611c0e612255565b6001600160e01b0319811663f23a6e6160e01b14611ce65760405162461bcd60e51b81526004018080602001828103825260288152602001806123516028913960400191505060405180910390fd5b60006120b0838361216d565b6120e657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aff565b506000610aff565b3b151590565b600061125c836001600160a01b038416612189565b8154600090821061214b5760405162461bcd60e51b815260040180806020018281038252602281526020018061232f6022913960400191505060405180910390fd5b82600001828154811061215a57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b6000818152600183016020526040812054801561224557835460001980830191908101906000908790839081106121bc57fe5b90600052602060002001549050808760000184815481106121d957fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061220957fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610aff565b6000915050610aff565b60e01c90565b600060443d101561226557611241565b600481823e6308c379a0612279825161224f565b1461228357611241565b6040513d600319016004823e80513d67ffffffffffffffff81602484011181841117156122b35750505050611241565b828401925082519150808211156122cd5750505050611241565b503d830160208284010111156122e557505050611241565b601f01601f191681016020016040529150509056fe455243313135353a207472616e7366657220746f206e6f6e2045524331313535526563656976657220696d706c656d656e746572456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473455243313135353a204552433131353552656365697665722072656a656374656420746f6b656e73416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74455243313135353a2062616c616e636520717565727920666f7220746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373455243313135353a2063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65455243313135353a207472616e7366657220746f20746865207a65726f2061646472657373455243313135353a207472616e736665722063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564455243313135353a20696e73756666696369656e742062616c616e636520666f72207472616e73666572455243313135353a2073657474696e6720617070726f76616c2073746174757320666f722073656c66455243313135353a206163636f756e747320616e6420696473206c656e677468206d69736d61746368455243313135353a2069647320616e6420616d6f756e7473206c656e677468206d69736d61746368455243313135353a206d696e7420746f20746865207a65726f2061646472657373416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a26469706673582212206b93266a963cefb5dcc7161758be463edca28a8dc8337e55beefbbabc25f14ee64736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THXERC20_LimitedSupply.json b/apps/api/src/app/hardhat/export/THXERC20_LimitedSupply.json new file mode 100644 index 000000000..3667d557e --- /dev/null +++ b/apps/api/src/app/hardhat/export/THXERC20_LimitedSupply.json @@ -0,0 +1,307 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THX_ERC20_LimitedSupply", + "sourceName": "contracts/utils/ERC20/THX_ERC20.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000e2538038062000e25833981810160405260808110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200010a57600080fd5b9083019060208201858111156200012057600080fd5b82516401000000008111828201881017156200013b57600080fd5b82525081516020918201929091019080838360005b838110156200016a57818101518382015260200162000150565b50505050905090810190601f168015620001985780820380516001836020036101000a031916815260200191505b50604090815260208281015192909101518651929450925085918591620001c59160039185019062000377565b508051620001db90600490602084019062000377565b50506005805460ff1916601217905550620001f7828262000201565b5050505062000423565b6001600160a01b0382166200025d576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6200026b6000838362000310565b62000287816002546200031560201b620005731790919060201c565b6002556001600160a01b03821660009081526020818152604090912054620002ba9183906200057362000315821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b60008282018381101562000370576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620003af5760008555620003fa565b82601f10620003ca57805160ff1916838001178555620003fa565b82800160010185558215620003fa579182015b82811115620003fa578251825591602001919060010190620003dd565b50620004089291506200040c565b5090565b5b808211156200040857600081556001016200040d565b6109f280620004336000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103f9565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610402565b6101736004803603602081101561021b57600080fd5b50356001600160a01b0316610450565b6100b661046b565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104cc565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610534565b610173600480360360408110156102a157600080fd5b506001600160a01b0381358116916020013516610548565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c6105d4565b84846105d8565b50600192915050565b60025490565b600061037f8484846106c4565b6103ef8461038b6105d4565b6103ea85604051806060016040528060288152602001610927602891396001600160a01b038a166000908152600160205260408120906103c96105d4565b6001600160a01b03168152602081019190915260400160002054919061081f565b6105d8565b5060019392505050565b60055460ff1690565b600061036361040f6105d4565b846103ea85600160006104206105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610573565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104d96105d4565b846103ea8560405180606001604052806025815260200161099860259139600160006105036105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061081f565b60006103636105416105d4565b84846106c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6000828201838110156105cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b03831661061d5760405162461bcd60e51b81526004018080602001828103825260248152602001806109746024913960400191505060405180910390fd5b6001600160a01b0382166106625760405162461bcd60e51b81526004018080602001828103825260228152602001806108df6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107095760405162461bcd60e51b815260040180806020018281038252602581526020018061094f6025913960400191505060405180910390fd5b6001600160a01b03821661074e5760405162461bcd60e51b81526004018080602001828103825260238152602001806108bc6023913960400191505060405180910390fd5b6107598383836108b6565b61079681604051806060016040528060268152602001610901602691396001600160a01b038616600090815260208190526040902054919061081f565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c59082610573565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108ae5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087357818101518382015260200161085b565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122092555631e3c671c3ad0e9398fc7e4198120e26816633db409891a7e02b19db9464736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103f9565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610402565b6101736004803603602081101561021b57600080fd5b50356001600160a01b0316610450565b6100b661046b565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104cc565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610534565b610173600480360360408110156102a157600080fd5b506001600160a01b0381358116916020013516610548565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c6105d4565b84846105d8565b50600192915050565b60025490565b600061037f8484846106c4565b6103ef8461038b6105d4565b6103ea85604051806060016040528060288152602001610927602891396001600160a01b038a166000908152600160205260408120906103c96105d4565b6001600160a01b03168152602081019190915260400160002054919061081f565b6105d8565b5060019392505050565b60055460ff1690565b600061036361040f6105d4565b846103ea85600160006104206105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610573565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104d96105d4565b846103ea8560405180606001604052806025815260200161099860259139600160006105036105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061081f565b60006103636105416105d4565b84846106c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6000828201838110156105cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b03831661061d5760405162461bcd60e51b81526004018080602001828103825260248152602001806109746024913960400191505060405180910390fd5b6001600160a01b0382166106625760405162461bcd60e51b81526004018080602001828103825260228152602001806108df6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107095760405162461bcd60e51b815260040180806020018281038252602581526020018061094f6025913960400191505060405180910390fd5b6001600160a01b03821661074e5760405162461bcd60e51b81526004018080602001828103825260238152602001806108bc6023913960400191505060405180910390fd5b6107598383836108b6565b61079681604051806060016040528060268152602001610901602691396001600160a01b038616600090815260208190526040902054919061081f565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c59082610573565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108ae5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087357818101518382015260200161085b565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122092555631e3c671c3ad0e9398fc7e4198120e26816633db409891a7e02b19db9464736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THXERC20_UnlimitedSupply.json b/apps/api/src/app/hardhat/export/THXERC20_UnlimitedSupply.json new file mode 100644 index 000000000..bf15f47d1 --- /dev/null +++ b/apps/api/src/app/hardhat/export/THXERC20_UnlimitedSupply.json @@ -0,0 +1,595 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THX_ERC20_UnlimitedSupply", + "sourceName": "contracts/utils/ERC20/THX_ERC20_UnlimitedSupply.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "address", + "name": "owner_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINTER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b50604051620019f7380380620019f7833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200010a57600080fd5b9083019060208201858111156200012057600080fd5b82516401000000008111828201881017156200013b57600080fd5b82525081516020918201929091019080838360005b838110156200016a57818101518382015260200162000150565b50505050905090810190601f168015620001985780820380516001836020036101000a031916815260200191505b5060405260209081015185519093508592508491620001bd91600391850190620004a9565b508051620001d3906004906020840190620004a9565b50506005805460ff19166012179055506000620001ef62000279565b600780546001600160a01b0319166001600160a01b03831690811790915560405191925090600090600080516020620019d7833981519152908290a35062000237816200027d565b6200024460008262000388565b620002707f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68262000388565b50505062000555565b3390565b6200028762000279565b6001600160a01b03166200029a62000398565b6001600160a01b031614620002f6576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166200033d5760405162461bcd60e51b8152600401808060200182810382526026815260200180620019b16026913960400191505060405180910390fd5b6007546040516001600160a01b03808416921690600080516020620019d783398151915290600090a3600780546001600160a01b0319166001600160a01b0392909216919091179055565b620003948282620003a7565b5050565b6007546001600160a01b031690565b6000828152600660209081526040909120620003ce91839062000b1d62000422821b17901c565b156200039457620003de62000279565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600062000439836001600160a01b03841662000442565b90505b92915050565b600062000450838362000491565b62000488575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200043c565b5060006200043c565b60009081526001919091016020526040902054151590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620004e157600085556200052c565b82601f10620004fc57805160ff19168380011785556200052c565b828001600101855582156200052c579182015b828111156200052c5782518255916020019190600101906200050f565b506200053a9291506200053e565b5090565b5b808211156200053a57600081556001016200053f565b61144c80620005656000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c80638da5cb5b116100c3578063a9059cbb1161007c578063a9059cbb146103fd578063ca15c87314610429578063d539139314610446578063d547741f1461044e578063dd62ed3e1461047a578063f2fde38b146104a85761014d565b80638da5cb5b1461034e5780639010d07c1461037257806391d148541461039557806395d89b41146103c1578063a217fddf146103c9578063a457c2d7146103d15761014d565b80632f2ff15d116101155780632f2ff15d1461027c578063313ce567146102aa57806336568abe146102c857806339509351146102f457806370a0823114610320578063715018a6146103465761014d565b806306fdde0314610152578063095ea7b3146101cf57806318160ddd1461020f57806323b872dd14610229578063248a9ca31461025f575b600080fd5b61015a6104ce565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561019457818101518382015260200161017c565b50505050905090810190601f1680156101c15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101fb600480360360408110156101e557600080fd5b506001600160a01b038135169060200135610564565b604080519115158252519081900360200190f35b610217610582565b60408051918252519081900360200190f35b6101fb6004803603606081101561023f57600080fd5b506001600160a01b03813581169160208101359091169060400135610588565b6102176004803603602081101561027557600080fd5b503561060f565b6102a86004803603604081101561029257600080fd5b50803590602001356001600160a01b0316610624565b005b6102b2610690565b6040805160ff9092168252519081900360200190f35b6102a8600480360360408110156102de57600080fd5b50803590602001356001600160a01b0316610699565b6101fb6004803603604081101561030a57600080fd5b506001600160a01b0381351690602001356106fa565b6102176004803603602081101561033657600080fd5b50356001600160a01b0316610748565b6102a8610763565b610356610821565b604080516001600160a01b039092168252519081900360200190f35b6103566004803603604081101561038857600080fd5b5080359060200135610830565b6101fb600480360360408110156103ab57600080fd5b50803590602001356001600160a01b031661084f565b61015a610867565b6102176108c8565b6101fb600480360360408110156103e757600080fd5b506001600160a01b0381351690602001356108cd565b6101fb6004803603604081101561041357600080fd5b506001600160a01b038135169060200135610935565b6102176004803603602081101561043f57600080fd5b5035610949565b610217610960565b6102a86004803603604081101561046457600080fd5b50803590602001356001600160a01b0316610984565b6102176004803603604081101561049057600080fd5b506001600160a01b03813581169160200135166109dd565b6102a8600480360360208110156104be57600080fd5b50356001600160a01b0316610a08565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561055a5780601f1061052f5761010080835404028352916020019161055a565b820191906000526020600020905b81548152906001019060200180831161053d57829003601f168201915b5050505050905090565b6000610578610571610b32565b8484610b36565b5060015b92915050565b60025490565b6000610595848484610c22565b610605846105a1610b32565b61060085604051806060016040528060288152602001611352602891396001600160a01b038a166000908152600160205260408120906105df610b32565b6001600160a01b031681526020810191909152604001600020549190610d7d565b610b36565b5060019392505050565b60009081526006602052604090206002015490565b60008281526006602052604090206002015461064790610642610b32565b61084f565b6106825760405162461bcd60e51b815260040180806020018281038252602f815260200180611285602f913960400191505060405180910390fd5b61068c8282610e14565b5050565b60055460ff1690565b6106a1610b32565b6001600160a01b0316816001600160a01b0316146106f05760405162461bcd60e51b815260040180806020018281038252602f8152602001806113e8602f913960400191505060405180910390fd5b61068c8282610e7d565b6000610578610707610b32565b846106008560016000610718610b32565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610ee6565b6001600160a01b031660009081526020819052604090205490565b61076b610b32565b6001600160a01b031661077c610821565b6001600160a01b0316146107d7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6007546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600780546001600160a01b0319169055565b6007546001600160a01b031690565b60008281526006602052604081206108489083610f40565b9392505050565b60008281526006602052604081206108489083610f4c565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561055a5780601f1061052f5761010080835404028352916020019161055a565b600081565b60006105786108da610b32565b84610600856040518060600160405280602581526020016113c36025913960016000610904610b32565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190610d7d565b6000610578610942610b32565b8484610c22565b600081815260066020526040812061057c90610f61565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6000828152600660205260409020600201546109a290610642610b32565b6106f05760405162461bcd60e51b81526004018080602001828103825260308152602001806113226030913960400191505060405180910390fd5b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b610a10610b32565b6001600160a01b0316610a21610821565b6001600160a01b031614610a7c576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116610ac15760405162461bcd60e51b81526004018080602001828103825260268152602001806112b46026913960400191505060405180910390fd5b6007546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600780546001600160a01b0319166001600160a01b0392909216919091179055565b6000610848836001600160a01b038416610f6c565b3390565b6001600160a01b038316610b7b5760405162461bcd60e51b815260040180806020018281038252602481526020018061139f6024913960400191505060405180910390fd5b6001600160a01b038216610bc05760405162461bcd60e51b81526004018080602001828103825260228152602001806112da6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b038316610c675760405162461bcd60e51b815260040180806020018281038252602581526020018061137a6025913960400191505060405180910390fd5b6001600160a01b038216610cac5760405162461bcd60e51b81526004018080602001828103825260238152602001806112626023913960400191505060405180910390fd5b610cb7838383610fb6565b610cf4816040518060600160405280602681526020016112fc602691396001600160a01b0386166000908152602081905260409020549190610d7d565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610d239082610ee6565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008184841115610e0c5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610dd1578181015183820152602001610db9565b50505050905090810190601f168015610dfe5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000828152600660205260409020610e2c9082610b1d565b1561068c57610e39610b32565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152600660205260409020610e959082610ff4565b1561068c57610ea2610b32565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b600082820183811015610848576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60006108488383611009565b6000610848836001600160a01b03841661106d565b600061057c82611085565b6000610f78838361106d565b610fae5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561057c565b50600061057c565b610fe07f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68461084f565b15610fef57610fef8382611089565b505050565b6000610848836001600160a01b038416611179565b8154600090821061104b5760405162461bcd60e51b81526004018080602001828103825260228152602001806112406022913960400191505060405180910390fd5b82600001828154811061105a57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b6001600160a01b0382166110e4576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6110f060008383610fb6565b6002546110fd9082610ee6565b6002556001600160a01b0382166000908152602081905260409020546111239082610ee6565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818152600183016020526040812054801561123557835460001980830191908101906000908790839081106111ac57fe5b90600052602060002001549050808760000184815481106111c957fe5b6000918252602080832090910192909255828152600189810190925260409020908401905586548790806111f957fe5b6001900381819060005260206000200160009055905586600101600087815260200190815260200160002060009055600194505050505061057c565b600091505061057c56fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e647345524332303a207472616e7366657220746f20746865207a65726f2061646472657373416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e744f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e6365416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b6545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220339e6347a85131212ad2bbc765e33c8918a3dd7d9e29b1fed5ada0541b2f1c2764736f6c634300070600334f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573738be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061014d5760003560e01c80638da5cb5b116100c3578063a9059cbb1161007c578063a9059cbb146103fd578063ca15c87314610429578063d539139314610446578063d547741f1461044e578063dd62ed3e1461047a578063f2fde38b146104a85761014d565b80638da5cb5b1461034e5780639010d07c1461037257806391d148541461039557806395d89b41146103c1578063a217fddf146103c9578063a457c2d7146103d15761014d565b80632f2ff15d116101155780632f2ff15d1461027c578063313ce567146102aa57806336568abe146102c857806339509351146102f457806370a0823114610320578063715018a6146103465761014d565b806306fdde0314610152578063095ea7b3146101cf57806318160ddd1461020f57806323b872dd14610229578063248a9ca31461025f575b600080fd5b61015a6104ce565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561019457818101518382015260200161017c565b50505050905090810190601f1680156101c15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101fb600480360360408110156101e557600080fd5b506001600160a01b038135169060200135610564565b604080519115158252519081900360200190f35b610217610582565b60408051918252519081900360200190f35b6101fb6004803603606081101561023f57600080fd5b506001600160a01b03813581169160208101359091169060400135610588565b6102176004803603602081101561027557600080fd5b503561060f565b6102a86004803603604081101561029257600080fd5b50803590602001356001600160a01b0316610624565b005b6102b2610690565b6040805160ff9092168252519081900360200190f35b6102a8600480360360408110156102de57600080fd5b50803590602001356001600160a01b0316610699565b6101fb6004803603604081101561030a57600080fd5b506001600160a01b0381351690602001356106fa565b6102176004803603602081101561033657600080fd5b50356001600160a01b0316610748565b6102a8610763565b610356610821565b604080516001600160a01b039092168252519081900360200190f35b6103566004803603604081101561038857600080fd5b5080359060200135610830565b6101fb600480360360408110156103ab57600080fd5b50803590602001356001600160a01b031661084f565b61015a610867565b6102176108c8565b6101fb600480360360408110156103e757600080fd5b506001600160a01b0381351690602001356108cd565b6101fb6004803603604081101561041357600080fd5b506001600160a01b038135169060200135610935565b6102176004803603602081101561043f57600080fd5b5035610949565b610217610960565b6102a86004803603604081101561046457600080fd5b50803590602001356001600160a01b0316610984565b6102176004803603604081101561049057600080fd5b506001600160a01b03813581169160200135166109dd565b6102a8600480360360208110156104be57600080fd5b50356001600160a01b0316610a08565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561055a5780601f1061052f5761010080835404028352916020019161055a565b820191906000526020600020905b81548152906001019060200180831161053d57829003601f168201915b5050505050905090565b6000610578610571610b32565b8484610b36565b5060015b92915050565b60025490565b6000610595848484610c22565b610605846105a1610b32565b61060085604051806060016040528060288152602001611352602891396001600160a01b038a166000908152600160205260408120906105df610b32565b6001600160a01b031681526020810191909152604001600020549190610d7d565b610b36565b5060019392505050565b60009081526006602052604090206002015490565b60008281526006602052604090206002015461064790610642610b32565b61084f565b6106825760405162461bcd60e51b815260040180806020018281038252602f815260200180611285602f913960400191505060405180910390fd5b61068c8282610e14565b5050565b60055460ff1690565b6106a1610b32565b6001600160a01b0316816001600160a01b0316146106f05760405162461bcd60e51b815260040180806020018281038252602f8152602001806113e8602f913960400191505060405180910390fd5b61068c8282610e7d565b6000610578610707610b32565b846106008560016000610718610b32565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610ee6565b6001600160a01b031660009081526020819052604090205490565b61076b610b32565b6001600160a01b031661077c610821565b6001600160a01b0316146107d7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6007546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600780546001600160a01b0319169055565b6007546001600160a01b031690565b60008281526006602052604081206108489083610f40565b9392505050565b60008281526006602052604081206108489083610f4c565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561055a5780601f1061052f5761010080835404028352916020019161055a565b600081565b60006105786108da610b32565b84610600856040518060600160405280602581526020016113c36025913960016000610904610b32565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190610d7d565b6000610578610942610b32565b8484610c22565b600081815260066020526040812061057c90610f61565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6000828152600660205260409020600201546109a290610642610b32565b6106f05760405162461bcd60e51b81526004018080602001828103825260308152602001806113226030913960400191505060405180910390fd5b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b610a10610b32565b6001600160a01b0316610a21610821565b6001600160a01b031614610a7c576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116610ac15760405162461bcd60e51b81526004018080602001828103825260268152602001806112b46026913960400191505060405180910390fd5b6007546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600780546001600160a01b0319166001600160a01b0392909216919091179055565b6000610848836001600160a01b038416610f6c565b3390565b6001600160a01b038316610b7b5760405162461bcd60e51b815260040180806020018281038252602481526020018061139f6024913960400191505060405180910390fd5b6001600160a01b038216610bc05760405162461bcd60e51b81526004018080602001828103825260228152602001806112da6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b038316610c675760405162461bcd60e51b815260040180806020018281038252602581526020018061137a6025913960400191505060405180910390fd5b6001600160a01b038216610cac5760405162461bcd60e51b81526004018080602001828103825260238152602001806112626023913960400191505060405180910390fd5b610cb7838383610fb6565b610cf4816040518060600160405280602681526020016112fc602691396001600160a01b0386166000908152602081905260409020549190610d7d565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610d239082610ee6565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008184841115610e0c5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610dd1578181015183820152602001610db9565b50505050905090810190601f168015610dfe5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000828152600660205260409020610e2c9082610b1d565b1561068c57610e39610b32565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152600660205260409020610e959082610ff4565b1561068c57610ea2610b32565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b600082820183811015610848576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60006108488383611009565b6000610848836001600160a01b03841661106d565b600061057c82611085565b6000610f78838361106d565b610fae5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561057c565b50600061057c565b610fe07f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68461084f565b15610fef57610fef8382611089565b505050565b6000610848836001600160a01b038416611179565b8154600090821061104b5760405162461bcd60e51b81526004018080602001828103825260228152602001806112406022913960400191505060405180910390fd5b82600001828154811061105a57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b6001600160a01b0382166110e4576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6110f060008383610fb6565b6002546110fd9082610ee6565b6002556001600160a01b0382166000908152602081905260409020546111239082610ee6565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818152600183016020526040812054801561123557835460001980830191908101906000908790839081106111ac57fe5b90600052602060002001549050808760000184815481106111c957fe5b6000918252602080832090910192909255828152600189810190925260409020908401905586548790806111f957fe5b6001900381819060005260206000200160009055905586600101600087815260200190815260200160002060009055600194505050505061057c565b600091505061057c56fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e647345524332303a207472616e7366657220746f20746865207a65726f2061646472657373416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e744f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e6365416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b6545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220339e6347a85131212ad2bbc765e33c8918a3dd7d9e29b1fed5ada0541b2f1c2764736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THXERC721.json b/apps/api/src/app/hardhat/export/THXERC721.json new file mode 100644 index 000000000..d2c1e7a57 --- /dev/null +++ b/apps/api/src/app/hardhat/export/THXERC721.json @@ -0,0 +1,753 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THX_ERC721", + "sourceName": "contracts/utils/ERC721/THX_ERC721.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "string", + "name": "baseURI_", + "type": "string" + }, + { + "internalType": "address", + "name": "owner_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINTER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "string", + "name": "_tokenURI", + "type": "string" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162002bf438038062002bf4833981810160405260808110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200010a57600080fd5b9083019060208201858111156200012057600080fd5b82516401000000008111828201881017156200013b57600080fd5b82525081516020918201929091019080838360005b838110156200016a57818101518382015260200162000150565b50505050905090810190601f168015620001985780820380516001836020036101000a031916815260200191505b5060405260200180516040519392919084640100000000821115620001bc57600080fd5b908301906020820185811115620001d257600080fd5b8251640100000000811182820188101715620001ed57600080fd5b82525081516020918201929091019080838360005b838110156200021c57818101518382015260200162000202565b50505050905090810190601f1680156200024a5780820380516001836020036101000a031916815260200191505b50604052602001519150849050836200026a6301ffc9a760e01b62000371565b81516200027f9060069060208501906200063b565b508051620002959060079060208401906200063b565b50620002a86380ac58cd60e01b62000371565b620002ba635b5e139f60e01b62000371565b620002cc63780e9d6360e01b62000371565b5060009050620002db620003f6565b600b80546001600160a01b0319166001600160a01b0383169081179091556040519192509060009060008051602062002bd4833981519152908290a3506200032381620003fa565b6200033060008262000505565b6200035c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68262000505565b620003678262000515565b50505050620006e7565b6001600160e01b03198082161415620003d1576040805162461bcd60e51b815260206004820152601c60248201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604482015290519081900360640190fd5b6001600160e01b0319166000908152602081905260409020805460ff19166001179055565b3390565b62000404620003f6565b6001600160a01b0316620004176200052a565b6001600160a01b03161462000473576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116620004ba5760405162461bcd60e51b815260040180806020018281038252602681526020018062002bae6026913960400191505060405180910390fd5b600b546040516001600160a01b0380841692169060008051602062002bd483398151915290600090a3600b80546001600160a01b0319166001600160a01b0392909216919091179055565b62000511828262000539565b5050565b8051620005119060099060208401906200063b565b600b546001600160a01b031690565b6000828152600a6020908152604090912062000560918390620013a9620005b4821b17901c565b15620005115762000570620003f6565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000620005cb836001600160a01b038416620005d4565b90505b92915050565b6000620005e2838362000623565b6200061a57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620005ce565b506000620005ce565b60009081526001919091016020526040902054151590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620006735760008555620006be565b82601f106200068e57805160ff1916838001178555620006be565b82800160010185558215620006be579182015b82811115620006be578251825591602001919060010190620006a1565b50620006cc929150620006d0565b5090565b5b80821115620006cc5760008155600101620006d1565b6124b780620006f76000396000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c8063715018a611610104578063b88d4fde116100a2578063d539139311610071578063d5391393146106e0578063d547741f146106e8578063e985e9c514610714578063f2fde38b14610742576101da565b8063b88d4fde1461052a578063c87b56dd146105f0578063ca15c8731461060d578063d0def5211461062a576101da565b806391d14854116100de57806391d14854146104c057806395d89b41146104ec578063a217fddf146104f4578063a22cb465146104fc576101da565b8063715018a61461048d5780638da5cb5b146104955780639010d07c1461049d576101da565b80632f2ff15d1161017c5780634f6ccce71161014b5780634f6ccce7146104255780636352211e146104425780636c0360eb1461045f57806370a0823114610467576101da565b80632f2ff15d1461036b5780632f745c591461039757806336568abe146103c357806342842e0e146103ef576101da565b8063095ea7b3116101b8578063095ea7b3146102d057806318160ddd146102fe57806323b872dd14610318578063248a9ca31461034e576101da565b806301ffc9a7146101df57806306fdde031461021a578063081812fc14610297575b600080fd5b610206600480360360208110156101f557600080fd5b50356001600160e01b031916610768565b604080519115158252519081900360200190f35b61022261078b565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561025c578181015183820152602001610244565b50505050905090810190601f1680156102895780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102b4600480360360208110156102ad57600080fd5b5035610821565b604080516001600160a01b039092168252519081900360200190f35b6102fc600480360360408110156102e657600080fd5b506001600160a01b038135169060200135610883565b005b61030661095e565b60408051918252519081900360200190f35b6102fc6004803603606081101561032e57600080fd5b506001600160a01b0381358116916020810135909116906040013561096f565b6103066004803603602081101561036457600080fd5b50356109c6565b6102fc6004803603604081101561038157600080fd5b50803590602001356001600160a01b03166109db565b610306600480360360408110156103ad57600080fd5b506001600160a01b038135169060200135610a47565b6102fc600480360360408110156103d957600080fd5b50803590602001356001600160a01b0316610a72565b6102fc6004803603606081101561040557600080fd5b506001600160a01b03813581169160208101359091169060400135610ad3565b6103066004803603602081101561043b57600080fd5b5035610aee565b6102b46004803603602081101561045857600080fd5b5035610b04565b610222610b2c565b6103066004803603602081101561047d57600080fd5b50356001600160a01b0316610b8d565b6102fc610bf5565b6102b4610cb3565b6102b4600480360360408110156104b357600080fd5b5080359060200135610cc2565b610206600480360360408110156104d657600080fd5b50803590602001356001600160a01b0316610cda565b610222610cf2565b610306610d53565b6102fc6004803603604081101561051257600080fd5b506001600160a01b0381351690602001351515610d58565b6102fc6004803603608081101561054057600080fd5b6001600160a01b0382358116926020810135909116916040820135919081019060808101606082013564010000000081111561057b57600080fd5b82018360208201111561058d57600080fd5b803590602001918460018302840111640100000000831117156105af57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610e5d945050505050565b6102226004803603602081101561060657600080fd5b5035610ebb565b6103066004803603602081101561062357600080fd5b503561113c565b6103066004803603604081101561064057600080fd5b6001600160a01b03823516919081019060408101602082013564010000000081111561066b57600080fd5b82018360208201111561067d57600080fd5b8035906020019184600183028401116401000000008311171561069f57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611153945050505050565b6103066111e9565b6102fc600480360360408110156106fe57600080fd5b50803590602001356001600160a01b031661120d565b6102066004803603604081101561072a57600080fd5b506001600160a01b0381358116916020013516611266565b6102fc6004803603602081101561075857600080fd5b50356001600160a01b0316611294565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b60068054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156108175780601f106107ec57610100808354040283529160200191610817565b820191906000526020600020905b8154815290600101906020018083116107fa57829003601f168201915b5050505050905090565b600061082c826113be565b6108675760405162461bcd60e51b815260040180806020018281038252602c815260200180612351602c913960400191505060405180910390fd5b506000908152600460205260409020546001600160a01b031690565b600061088e82610b04565b9050806001600160a01b0316836001600160a01b031614156108e15760405162461bcd60e51b81526004018080602001828103825260218152602001806124016021913960400191505060405180910390fd5b806001600160a01b03166108f36113cb565b6001600160a01b0316148061091457506109148161090f6113cb565b611266565b61094f5760405162461bcd60e51b81526004018080602001828103825260388152602001806122a46038913960400191505060405180910390fd5b61095983836113cf565b505050565b600061096a600261143d565b905090565b61098061097a6113cb565b82611448565b6109bb5760405162461bcd60e51b81526004018080602001828103825260318152602001806124226031913960400191505060405180910390fd5b6109598383836114ec565b6000908152600a602052604090206002015490565b6000828152600a60205260409020600201546109fe906109f96113cb565b610cda565b610a395760405162461bcd60e51b815260040180806020018281038252602f81526020018061219d602f913960400191505060405180910390fd5b610a438282611638565b5050565b6001600160a01b0382166000908152600160205260408120610a6990836116a1565b90505b92915050565b610a7a6113cb565b6001600160a01b0316816001600160a01b031614610ac95760405162461bcd60e51b815260040180806020018281038252602f815260200180612453602f913960400191505060405180910390fd5b610a4382826116ad565b61095983838360405180602001604052806000815250610e5d565b600080610afc600284611716565b509392505050565b6000610a6c826040518060600160405280602981526020016123066029913960029190611732565b60098054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156108175780601f106107ec57610100808354040283529160200191610817565b60006001600160a01b038216610bd45760405162461bcd60e51b815260040180806020018281038252602a8152602001806122dc602a913960400191505060405180910390fd5b6001600160a01b0382166000908152600160205260409020610a6c9061143d565b610bfd6113cb565b6001600160a01b0316610c0e610cb3565b6001600160a01b031614610c69576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600b546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600b80546001600160a01b0319169055565b600b546001600160a01b031690565b6000828152600a60205260408120610a6990836116a1565b6000828152600a60205260408120610a699083611749565b60078054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156108175780601f106107ec57610100808354040283529160200191610817565b600081565b610d606113cb565b6001600160a01b0316826001600160a01b03161415610dc6576040805162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015290519081900360640190fd5b8060056000610dd36113cb565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff191692151592909217909155610e176113cb565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c318360405180821515815260200191505060405180910390a35050565b610e6e610e686113cb565b83611448565b610ea95760405162461bcd60e51b81526004018080602001828103825260318152602001806124226031913960400191505060405180910390fd5b610eb58484848461175e565b50505050565b6060610ec6826113be565b610f015760405162461bcd60e51b815260040180806020018281038252602f8152602001806123d2602f913960400191505060405180910390fd5b60008281526008602090815260408083208054825160026001831615610100026000190190921691909104601f810185900485028201850190935282815292909190830182828015610f945780601f10610f6957610100808354040283529160200191610f94565b820191906000526020600020905b815481529060010190602001808311610f7757829003601f168201915b505050505090506000610fa5610b2c565b9050805160001415610fb957509050610786565b81511561107a5780826040516020018083805190602001908083835b60208310610ff45780518252601f199092019160209182019101610fd5565b51815160209384036101000a600019018019909216911617905285519190930192850191508083835b6020831061103c5780518252601f19909201916020918201910161101d565b6001836020036101000a0380198251168184511680821785525050505050509050019250505060405160208183030381529060405292505050610786565b80611084856117b0565b6040516020018083805190602001908083835b602083106110b65780518252601f199092019160209182019101611097565b51815160209384036101000a600019018019909216911617905285519190930192850191508083835b602083106110fe5780518252601f1990920191602091820191016110df565b6001836020036101000a0380198251168184511680821785525050505050509050019250505060405160208183030381529060405292505050919050565b6000818152600a60205260408120610a6c9061143d565b600061117f7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633610cda565b6111bd576040805162461bcd60e51b815260206004820152600a6024820152692727aa2fa6a4a72a22a960b11b604482015290519081900360640190fd5b6111c7600c61188b565b60006111d3600c611894565b90506111df8482611898565b610a6981846119c6565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6000828152600a602052604090206002015461122b906109f96113cb565b610ac95760405162461bcd60e51b81526004018080602001828103825260308152602001806122746030913960400191505060405180910390fd5b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b61129c6113cb565b6001600160a01b03166112ad610cb3565b6001600160a01b031614611308576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b03811661134d5760405162461bcd60e51b81526004018080602001828103825260268152602001806121fe6026913960400191505060405180910390fd5b600b546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6000610a69836001600160a01b038416611a29565b6000610a6c600283611a73565b3390565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061140482610b04565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000610a6c82611894565b6000611453826113be565b61148e5760405162461bcd60e51b815260040180806020018281038252602c815260200180612248602c913960400191505060405180910390fd5b600061149983610b04565b9050806001600160a01b0316846001600160a01b031614806114d45750836001600160a01b03166114c984610821565b6001600160a01b0316145b806114e457506114e48185611266565b949350505050565b826001600160a01b03166114ff82610b04565b6001600160a01b0316146115445760405162461bcd60e51b81526004018080602001828103825260298152602001806123a96029913960400191505060405180910390fd5b6001600160a01b0382166115895760405162461bcd60e51b81526004018080602001828103825260248152602001806122246024913960400191505060405180910390fd5b611594838383610959565b61159f6000826113cf565b6001600160a01b03831660009081526001602052604090206115c19082611a7f565b506001600160a01b03821660009081526001602052604090206115e49082611a8b565b506115f160028284611a97565b5080826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000828152600a6020526040902061165090826113a9565b15610a435761165d6113cb565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000610a698383611aad565b6000828152600a602052604090206116c59082611b11565b15610a43576116d26113cb565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b60008080806117258686611b26565b9097909650945050505050565b600061173f848484611ba1565b90505b9392505050565b6000610a69836001600160a01b038416611c6b565b6117698484846114ec565b61177584848484611c83565b610eb55760405162461bcd60e51b81526004018080602001828103825260328152602001806121cc6032913960400191505060405180910390fd5b6060816117d557506040805180820190915260018152600360fc1b6020820152610786565b8160005b81156117ed57600101600a820491506117d9565b60008167ffffffffffffffff8111801561180657600080fd5b506040519080825280601f01601f191660200182016040528015611831576020820181803683370190505b50859350905060001982015b831561188257600a840660300160f81b8282806001900393508151811061186057fe5b60200101906001600160f81b031916908160001a905350600a8404935061183d565b50949350505050565b80546001019055565b5490565b6001600160a01b0382166118f3576040805162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015290519081900360640190fd5b6118fc816113be565b1561194e576040805162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015290519081900360640190fd5b61195a60008383610959565b6001600160a01b038216600090815260016020526040902061197c9082611a8b565b5061198960028284611a97565b5060405181906001600160a01b038416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6119cf826113be565b611a0a5760405162461bcd60e51b815260040180806020018281038252602c81526020018061237d602c913960400191505060405180910390fd5b60008281526008602090815260409091208251610959928401906120d9565b6000611a358383611c6b565b611a6b57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610a6c565b506000610a6c565b6000610a698383611c6b565b6000610a698383611deb565b6000610a698383611a29565b600061173f84846001600160a01b038516611eb1565b81546000908210611aef5760405162461bcd60e51b815260040180806020018281038252602281526020018061217b6022913960400191505060405180910390fd5b826000018281548110611afe57fe5b9060005260206000200154905092915050565b6000610a69836001600160a01b038416611deb565b815460009081908310611b6a5760405162461bcd60e51b815260040180806020018281038252602281526020018061232f6022913960400191505060405180910390fd5b6000846000018481548110611b7b57fe5b906000526020600020906002020190508060000154816001015492509250509250929050565b60008281526001840160205260408120548281611c3c5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611c01578181015183820152602001611be9565b50505050905090810190601f168015611c2e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50846000016001820381548110611c4f57fe5b9060005260206000209060020201600101549150509392505050565b60009081526001919091016020526040902054151590565b6000611c97846001600160a01b0316611f48565b611ca3575060016114e4565b6000611db1630a85bd0160e11b611cb86113cb565b88878760405160240180856001600160a01b03168152602001846001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611d1f578181015183820152602001611d07565b50505050905090810190601f168015611d4c5780820380516001836020036101000a031916815260200191505b5095505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518060600160405280603281526020016121cc603291396001600160a01b0388169190611f4e565b90506000818060200190516020811015611dca57600080fd5b50516001600160e01b031916630a85bd0160e11b1492505050949350505050565b60008181526001830160205260408120548015611ea75783546000198083019190810190600090879083908110611e1e57fe5b9060005260206000200154905080876000018481548110611e3b57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080611e6b57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610a6c565b6000915050610a6c565b600082815260018401602052604081205480611f16575050604080518082018252838152602080820184815286546001818101895560008981528481209551600290930290950191825591519082015586548684528188019092529290912055611742565b82856000016001830381548110611f2957fe5b9060005260206000209060020201600101819055506000915050611742565b3b151590565b606061173f848460008585611f6285611f48565b611fb3576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310611ff15780518252601f199092019160209182019101611fd2565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612053576040519150601f19603f3d011682016040523d82523d6000602084013e612058565b606091505b5091509150612068828286612073565b979650505050505050565b60608315612082575081611742565b8251156120925782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315611c01578181015183820152602001611be9565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261210f5760008555612155565b82601f1061212857805160ff1916838001178555612155565b82800160010185558215612155579182015b8281111561215557825182559160200191906001019061213a565b50612161929150612165565b5090565b5b80821115612161576000815560010161216656fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e744552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e7465724f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734552433732313a207472616e7366657220746f20746865207a65726f20616464726573734552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b654552433732313a20617070726f76652063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656420666f7220616c6c4552433732313a2062616c616e636520717565727920666f7220746865207a65726f20616464726573734552433732313a206f776e657220717565727920666f72206e6f6e6578697374656e7420746f6b656e456e756d657261626c654d61703a20696e646578206f7574206f6620626f756e64734552433732313a20617070726f76656420717565727920666f72206e6f6e6578697374656e7420746f6b656e4552433732314d657461646174613a2055524920736574206f66206e6f6e6578697374656e7420746f6b656e4552433732313a207472616e73666572206f6620746f6b656e2074686174206973206e6f74206f776e4552433732314d657461646174613a2055524920717565727920666f72206e6f6e6578697374656e7420746f6b656e4552433732313a20617070726f76616c20746f2063757272656e74206f776e65724552433732313a207472616e736665722063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220824b5c0669df2c3d081e845da5e34644edc08bd41f5f0a02b09c19b0a3c1329164736f6c634300070600334f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573738be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101da5760003560e01c8063715018a611610104578063b88d4fde116100a2578063d539139311610071578063d5391393146106e0578063d547741f146106e8578063e985e9c514610714578063f2fde38b14610742576101da565b8063b88d4fde1461052a578063c87b56dd146105f0578063ca15c8731461060d578063d0def5211461062a576101da565b806391d14854116100de57806391d14854146104c057806395d89b41146104ec578063a217fddf146104f4578063a22cb465146104fc576101da565b8063715018a61461048d5780638da5cb5b146104955780639010d07c1461049d576101da565b80632f2ff15d1161017c5780634f6ccce71161014b5780634f6ccce7146104255780636352211e146104425780636c0360eb1461045f57806370a0823114610467576101da565b80632f2ff15d1461036b5780632f745c591461039757806336568abe146103c357806342842e0e146103ef576101da565b8063095ea7b3116101b8578063095ea7b3146102d057806318160ddd146102fe57806323b872dd14610318578063248a9ca31461034e576101da565b806301ffc9a7146101df57806306fdde031461021a578063081812fc14610297575b600080fd5b610206600480360360208110156101f557600080fd5b50356001600160e01b031916610768565b604080519115158252519081900360200190f35b61022261078b565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561025c578181015183820152602001610244565b50505050905090810190601f1680156102895780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102b4600480360360208110156102ad57600080fd5b5035610821565b604080516001600160a01b039092168252519081900360200190f35b6102fc600480360360408110156102e657600080fd5b506001600160a01b038135169060200135610883565b005b61030661095e565b60408051918252519081900360200190f35b6102fc6004803603606081101561032e57600080fd5b506001600160a01b0381358116916020810135909116906040013561096f565b6103066004803603602081101561036457600080fd5b50356109c6565b6102fc6004803603604081101561038157600080fd5b50803590602001356001600160a01b03166109db565b610306600480360360408110156103ad57600080fd5b506001600160a01b038135169060200135610a47565b6102fc600480360360408110156103d957600080fd5b50803590602001356001600160a01b0316610a72565b6102fc6004803603606081101561040557600080fd5b506001600160a01b03813581169160208101359091169060400135610ad3565b6103066004803603602081101561043b57600080fd5b5035610aee565b6102b46004803603602081101561045857600080fd5b5035610b04565b610222610b2c565b6103066004803603602081101561047d57600080fd5b50356001600160a01b0316610b8d565b6102fc610bf5565b6102b4610cb3565b6102b4600480360360408110156104b357600080fd5b5080359060200135610cc2565b610206600480360360408110156104d657600080fd5b50803590602001356001600160a01b0316610cda565b610222610cf2565b610306610d53565b6102fc6004803603604081101561051257600080fd5b506001600160a01b0381351690602001351515610d58565b6102fc6004803603608081101561054057600080fd5b6001600160a01b0382358116926020810135909116916040820135919081019060808101606082013564010000000081111561057b57600080fd5b82018360208201111561058d57600080fd5b803590602001918460018302840111640100000000831117156105af57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610e5d945050505050565b6102226004803603602081101561060657600080fd5b5035610ebb565b6103066004803603602081101561062357600080fd5b503561113c565b6103066004803603604081101561064057600080fd5b6001600160a01b03823516919081019060408101602082013564010000000081111561066b57600080fd5b82018360208201111561067d57600080fd5b8035906020019184600183028401116401000000008311171561069f57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611153945050505050565b6103066111e9565b6102fc600480360360408110156106fe57600080fd5b50803590602001356001600160a01b031661120d565b6102066004803603604081101561072a57600080fd5b506001600160a01b0381358116916020013516611266565b6102fc6004803603602081101561075857600080fd5b50356001600160a01b0316611294565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b60068054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156108175780601f106107ec57610100808354040283529160200191610817565b820191906000526020600020905b8154815290600101906020018083116107fa57829003601f168201915b5050505050905090565b600061082c826113be565b6108675760405162461bcd60e51b815260040180806020018281038252602c815260200180612351602c913960400191505060405180910390fd5b506000908152600460205260409020546001600160a01b031690565b600061088e82610b04565b9050806001600160a01b0316836001600160a01b031614156108e15760405162461bcd60e51b81526004018080602001828103825260218152602001806124016021913960400191505060405180910390fd5b806001600160a01b03166108f36113cb565b6001600160a01b0316148061091457506109148161090f6113cb565b611266565b61094f5760405162461bcd60e51b81526004018080602001828103825260388152602001806122a46038913960400191505060405180910390fd5b61095983836113cf565b505050565b600061096a600261143d565b905090565b61098061097a6113cb565b82611448565b6109bb5760405162461bcd60e51b81526004018080602001828103825260318152602001806124226031913960400191505060405180910390fd5b6109598383836114ec565b6000908152600a602052604090206002015490565b6000828152600a60205260409020600201546109fe906109f96113cb565b610cda565b610a395760405162461bcd60e51b815260040180806020018281038252602f81526020018061219d602f913960400191505060405180910390fd5b610a438282611638565b5050565b6001600160a01b0382166000908152600160205260408120610a6990836116a1565b90505b92915050565b610a7a6113cb565b6001600160a01b0316816001600160a01b031614610ac95760405162461bcd60e51b815260040180806020018281038252602f815260200180612453602f913960400191505060405180910390fd5b610a4382826116ad565b61095983838360405180602001604052806000815250610e5d565b600080610afc600284611716565b509392505050565b6000610a6c826040518060600160405280602981526020016123066029913960029190611732565b60098054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156108175780601f106107ec57610100808354040283529160200191610817565b60006001600160a01b038216610bd45760405162461bcd60e51b815260040180806020018281038252602a8152602001806122dc602a913960400191505060405180910390fd5b6001600160a01b0382166000908152600160205260409020610a6c9061143d565b610bfd6113cb565b6001600160a01b0316610c0e610cb3565b6001600160a01b031614610c69576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600b546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600b80546001600160a01b0319169055565b600b546001600160a01b031690565b6000828152600a60205260408120610a6990836116a1565b6000828152600a60205260408120610a699083611749565b60078054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156108175780601f106107ec57610100808354040283529160200191610817565b600081565b610d606113cb565b6001600160a01b0316826001600160a01b03161415610dc6576040805162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015290519081900360640190fd5b8060056000610dd36113cb565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff191692151592909217909155610e176113cb565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c318360405180821515815260200191505060405180910390a35050565b610e6e610e686113cb565b83611448565b610ea95760405162461bcd60e51b81526004018080602001828103825260318152602001806124226031913960400191505060405180910390fd5b610eb58484848461175e565b50505050565b6060610ec6826113be565b610f015760405162461bcd60e51b815260040180806020018281038252602f8152602001806123d2602f913960400191505060405180910390fd5b60008281526008602090815260408083208054825160026001831615610100026000190190921691909104601f810185900485028201850190935282815292909190830182828015610f945780601f10610f6957610100808354040283529160200191610f94565b820191906000526020600020905b815481529060010190602001808311610f7757829003601f168201915b505050505090506000610fa5610b2c565b9050805160001415610fb957509050610786565b81511561107a5780826040516020018083805190602001908083835b60208310610ff45780518252601f199092019160209182019101610fd5565b51815160209384036101000a600019018019909216911617905285519190930192850191508083835b6020831061103c5780518252601f19909201916020918201910161101d565b6001836020036101000a0380198251168184511680821785525050505050509050019250505060405160208183030381529060405292505050610786565b80611084856117b0565b6040516020018083805190602001908083835b602083106110b65780518252601f199092019160209182019101611097565b51815160209384036101000a600019018019909216911617905285519190930192850191508083835b602083106110fe5780518252601f1990920191602091820191016110df565b6001836020036101000a0380198251168184511680821785525050505050509050019250505060405160208183030381529060405292505050919050565b6000818152600a60205260408120610a6c9061143d565b600061117f7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633610cda565b6111bd576040805162461bcd60e51b815260206004820152600a6024820152692727aa2fa6a4a72a22a960b11b604482015290519081900360640190fd5b6111c7600c61188b565b60006111d3600c611894565b90506111df8482611898565b610a6981846119c6565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6000828152600a602052604090206002015461122b906109f96113cb565b610ac95760405162461bcd60e51b81526004018080602001828103825260308152602001806122746030913960400191505060405180910390fd5b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b61129c6113cb565b6001600160a01b03166112ad610cb3565b6001600160a01b031614611308576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b03811661134d5760405162461bcd60e51b81526004018080602001828103825260268152602001806121fe6026913960400191505060405180910390fd5b600b546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6000610a69836001600160a01b038416611a29565b6000610a6c600283611a73565b3390565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061140482610b04565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000610a6c82611894565b6000611453826113be565b61148e5760405162461bcd60e51b815260040180806020018281038252602c815260200180612248602c913960400191505060405180910390fd5b600061149983610b04565b9050806001600160a01b0316846001600160a01b031614806114d45750836001600160a01b03166114c984610821565b6001600160a01b0316145b806114e457506114e48185611266565b949350505050565b826001600160a01b03166114ff82610b04565b6001600160a01b0316146115445760405162461bcd60e51b81526004018080602001828103825260298152602001806123a96029913960400191505060405180910390fd5b6001600160a01b0382166115895760405162461bcd60e51b81526004018080602001828103825260248152602001806122246024913960400191505060405180910390fd5b611594838383610959565b61159f6000826113cf565b6001600160a01b03831660009081526001602052604090206115c19082611a7f565b506001600160a01b03821660009081526001602052604090206115e49082611a8b565b506115f160028284611a97565b5080826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000828152600a6020526040902061165090826113a9565b15610a435761165d6113cb565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000610a698383611aad565b6000828152600a602052604090206116c59082611b11565b15610a43576116d26113cb565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b60008080806117258686611b26565b9097909650945050505050565b600061173f848484611ba1565b90505b9392505050565b6000610a69836001600160a01b038416611c6b565b6117698484846114ec565b61177584848484611c83565b610eb55760405162461bcd60e51b81526004018080602001828103825260328152602001806121cc6032913960400191505060405180910390fd5b6060816117d557506040805180820190915260018152600360fc1b6020820152610786565b8160005b81156117ed57600101600a820491506117d9565b60008167ffffffffffffffff8111801561180657600080fd5b506040519080825280601f01601f191660200182016040528015611831576020820181803683370190505b50859350905060001982015b831561188257600a840660300160f81b8282806001900393508151811061186057fe5b60200101906001600160f81b031916908160001a905350600a8404935061183d565b50949350505050565b80546001019055565b5490565b6001600160a01b0382166118f3576040805162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015290519081900360640190fd5b6118fc816113be565b1561194e576040805162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015290519081900360640190fd5b61195a60008383610959565b6001600160a01b038216600090815260016020526040902061197c9082611a8b565b5061198960028284611a97565b5060405181906001600160a01b038416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6119cf826113be565b611a0a5760405162461bcd60e51b815260040180806020018281038252602c81526020018061237d602c913960400191505060405180910390fd5b60008281526008602090815260409091208251610959928401906120d9565b6000611a358383611c6b565b611a6b57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610a6c565b506000610a6c565b6000610a698383611c6b565b6000610a698383611deb565b6000610a698383611a29565b600061173f84846001600160a01b038516611eb1565b81546000908210611aef5760405162461bcd60e51b815260040180806020018281038252602281526020018061217b6022913960400191505060405180910390fd5b826000018281548110611afe57fe5b9060005260206000200154905092915050565b6000610a69836001600160a01b038416611deb565b815460009081908310611b6a5760405162461bcd60e51b815260040180806020018281038252602281526020018061232f6022913960400191505060405180910390fd5b6000846000018481548110611b7b57fe5b906000526020600020906002020190508060000154816001015492509250509250929050565b60008281526001840160205260408120548281611c3c5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611c01578181015183820152602001611be9565b50505050905090810190601f168015611c2e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50846000016001820381548110611c4f57fe5b9060005260206000209060020201600101549150509392505050565b60009081526001919091016020526040902054151590565b6000611c97846001600160a01b0316611f48565b611ca3575060016114e4565b6000611db1630a85bd0160e11b611cb86113cb565b88878760405160240180856001600160a01b03168152602001846001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611d1f578181015183820152602001611d07565b50505050905090810190601f168015611d4c5780820380516001836020036101000a031916815260200191505b5095505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518060600160405280603281526020016121cc603291396001600160a01b0388169190611f4e565b90506000818060200190516020811015611dca57600080fd5b50516001600160e01b031916630a85bd0160e11b1492505050949350505050565b60008181526001830160205260408120548015611ea75783546000198083019190810190600090879083908110611e1e57fe5b9060005260206000200154905080876000018481548110611e3b57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080611e6b57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610a6c565b6000915050610a6c565b600082815260018401602052604081205480611f16575050604080518082018252838152602080820184815286546001818101895560008981528481209551600290930290950191825591519082015586548684528188019092529290912055611742565b82856000016001830381548110611f2957fe5b9060005260206000209060020201600101819055506000915050611742565b3b151590565b606061173f848460008585611f6285611f48565b611fb3576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310611ff15780518252601f199092019160209182019101611fd2565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612053576040519150601f19603f3d011682016040523d82523d6000602084013e612058565b606091505b5091509150612068828286612073565b979650505050505050565b60608315612082575081611742565b8251156120925782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315611c01578181015183820152602001611be9565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261210f5760008555612155565b82601f1061212857805160ff1916838001178555612155565b82800160010185558215612155579182015b8281111561215557825182559160200191906001019061213a565b50612161929150612165565b5090565b5b80821115612161576000815560010161216656fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e744552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e7465724f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734552433732313a207472616e7366657220746f20746865207a65726f20616464726573734552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b654552433732313a20617070726f76652063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656420666f7220616c6c4552433732313a2062616c616e636520717565727920666f7220746865207a65726f20616464726573734552433732313a206f776e657220717565727920666f72206e6f6e6578697374656e7420746f6b656e456e756d657261626c654d61703a20696e646578206f7574206f6620626f756e64734552433732313a20617070726f76656420717565727920666f72206e6f6e6578697374656e7420746f6b656e4552433732314d657461646174613a2055524920736574206f66206e6f6e6578697374656e7420746f6b656e4552433732313a207472616e73666572206f6620746f6b656e2074686174206973206e6f74206f776e4552433732314d657461646174613a2055524920717565727920666f72206e6f6e6578697374656e7420746f6b656e4552433732313a20617070726f76616c20746f2063757272656e74206f776e65724552433732313a207472616e736665722063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220824b5c0669df2c3d081e845da5e34644edc08bd41f5f0a02b09c19b0a3c1329164736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THXPaymentSplitter.json b/apps/api/src/app/hardhat/export/THXPaymentSplitter.json new file mode 100644 index 000000000..db415a73b --- /dev/null +++ b/apps/api/src/app/hardhat/export/THXPaymentSplitter.json @@ -0,0 +1,184 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THXPaymentSplitter", + "sourceName": "contracts/utils/THXPaymentSplitter.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + }, + { + "internalType": "address", + "name": "_registry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bpt", + "outputs": [ + { + "internalType": "contract IWeightedPool", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOut", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "gauge", + "outputs": [ + { + "internalType": "contract IGauge", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paymentToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "rates", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract ITHXRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_rate", + "type": "uint256" + } + ], + "name": "setRate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_registry", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vault", + "outputs": [ + { + "internalType": "contract BalancerVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b506040516200148138038062001481833981016040819052620000349162000304565b6001600081905580546001600160a01b038085166001600160a01b03199283161790925560028054848416921691909117908190556040805163d41c3a6560e01b81529051919092169163d41c3a65916004808301926020929190829003018186803b158015620000a457600080fd5b505afa158015620000b9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000df9190620002e0565b600680546001600160a01b0319166001600160a01b039283161790556002546040805163f2803f0360e01b81529051919092169163f2803f03916004808301926020929190829003018186803b1580156200013957600080fd5b505afa1580156200014e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001749190620002e0565b600380546001600160a01b0319166001600160a01b03928316179081905560408051634163183360e11b8152905191909216916382c63066916004808301926020929190829003018186803b158015620001cd57600080fd5b505afa158015620001e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002089190620002e0565b600480546001600160a01b0319166001600160a01b0392831617808255604080516311b2515f60e31b815290519190931692638d928af89281810192602092909190829003018186803b1580156200025f57600080fd5b505afa15801562000274573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200029a9190620002e0565b600580546001600160a01b0319166001600160a01b0392909216919091179055506200033b9050565b80516001600160a01b0381168114620002db57600080fd5b919050565b600060208284031215620002f2578081fd5b620002fd82620002c3565b9392505050565b6000806040838503121562000317578081fd5b6200032283620002c3565b91506200033260208401620002c3565b90509250929050565b611136806200034b6000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c80637b103999116100665780637b10399914610111578063a6f19c8414610119578063a8734f0b14610121578063a91ee0dc14610134578063fbfa77cf146101475761009e565b80630efe6a8b146100a35780632bdb7097146100b85780633013ce29146100cb578063546af3c3146100e957806370a08231146100f1575b600080fd5b6100b66100b1366004610de6565b61014f565b005b6100b66100c6366004610dbb565b610a46565b6100d3610a9c565b6040516100e09190610edd565b60405180910390f35b6100d3610aab565b6101046100ff366004610d83565b610aba565b6040516100e091906110be565b6100d3610b64565b6100d3610b73565b61010461012f366004610d83565b610b82565b6100b6610142366004610d83565b610b94565b6100d3610bf0565b600260005414156101a7576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260009081556001600160a01b0384168152600760205260409020546101e95760405162461bcd60e51b81526004016101e090611057565b60405180910390fd5b6006546040516323b872dd60e01b81526001600160a01b03909116906323b872dd9061021d90869030908790600401610ef1565b602060405180830381600087803b15801561023757600080fd5b505af115801561024b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061026f9190610e1a565b61028b5760405162461bcd60e51b81526004016101e090611022565b60025460408051637a3d22ad60e11b815290516000926001600160a01b03169163f47a455a916004808301926020929190829003018186803b1580156102d057600080fd5b505afa1580156102e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103089190610e3a565b9050600061032261271061031c8685610bff565b90610c61565b6006546002546040805163cd44673560e01b815290519394506001600160a01b039283169363a9059cbb939092169163cd44673591600480820192602092909190829003018186803b15801561037757600080fd5b505afa15801561038b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103af9190610d9f565b836040518363ffffffff1660e01b81526004016103cd929190610f15565b602060405180830381600087803b1580156103e757600080fd5b505af11580156103fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041f9190610e1a565b61043b5760405162461bcd60e51b81526004016101e090611022565b604080516002808252606082018352600092602083019080368337505060065482519293506001600160a01b03169183915060009061047657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506000816001815181106104a557fe5b6001600160a01b039290921660209283029190910182015260408051600280825260608201835260009391929091830190803683370190505090506104ea8684610cc8565b816000815181106104f757fe5b60200260200101818152505060008160018151811061051257fe5b602090810291909101015260065460055482516001600160a01b039283169263095ea7b3921690849060009061054457fe5b60200260200101516040518363ffffffff1660e01b8152600401610569929190610f15565b602060405180830381600087803b15801561058357600080fd5b505af1158015610597573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105bb9190610e1a565b50600554600480546040805163038fff2d60e41b815290516001600160a01b039485169463b95cac28949316926338fff2d092808201926020929091829003018186803b15801561060b57600080fd5b505afa15801561061f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106439190610e3a565b303060405180608001604052808881526020018781526020016001888d60405160200161067293929190610ff6565b6040516020818303038152906040528152602001600015158152506040518563ffffffff1660e01b81526004016106ac9493929190610f2e565b600060405180830381600087803b1580156106c657600080fd5b505af11580156106da573d6000803e3d6000fd5b5050600480546040516370a0823160e01b8152600094506001600160a01b0390911692506370a082319161071091309101610edd565b60206040518083038186803b15801561072857600080fd5b505afa15801561073c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107609190610e3a565b6004805460035460405163095ea7b360e01b81529394506001600160a01b039182169363095ea7b3936107999390921691869101610f15565b602060405180830381600087803b1580156107b357600080fd5b505af11580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190610e1a565b5060035460405163b6b55f2560e01b81526001600160a01b039091169063b6b55f259061081c9084906004016110be565b600060405180830381600087803b15801561083657600080fd5b505af115801561084a573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190610881903090600401610edd565b60206040518083038186803b15801561089957600080fd5b505afa1580156108ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d19190610e3a565b600354600254604080516367906c3960e11b815290519394506001600160a01b039283169363a9059cbb939092169163cf20d87291600480820192602092909190829003018186803b15801561092657600080fd5b505afa15801561093a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095e9190610d9f565b836040518363ffffffff1660e01b815260040161097c929190610f15565b602060405180830381600087803b15801561099657600080fd5b505af11580156109aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ce9190610e1a565b6109ea5760405162461bcd60e51b81526004016101e090611022565b6001600160a01b038916600090815260086020526040902054610a0d9089610d25565b6001600160a01b0390991660009081526008602090815260408083209b909b556009905298892042905550506001909655505050505050565b6001546001600160a01b0316610a5a610d7f565b6001600160a01b031614610a805760405162461bcd60e51b81526004016101e090611087565b6001600160a01b03909116600090815260076020526040902055565b6006546001600160a01b031681565b6004546001600160a01b031681565b6001600160a01b038116600090815260076020908152604080832054600990925282205480610aee57600092505050610b5f565b426000610b0584610aff8486610cc8565b90610bff565b6001600160a01b038716600090815260086020526040902054909150811115610b35576000945050505050610b5f565b6001600160a01b038616600090815260086020526040902054610b589082610cc8565b9450505050505b919050565b6002546001600160a01b031681565b6003546001600160a01b031681565b60076020526000908152604090205481565b6001546001600160a01b0316610ba8610d7f565b6001600160a01b031614610bce5760405162461bcd60e51b81526004016101e090611087565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6005546001600160a01b031681565b600082610c0e57506000610c5b565b82820282848281610c1b57fe5b0414610c585760405162461bcd60e51b81526004018080602001828103825260218152602001806110e06021913960400191505060405180910390fd5b90505b92915050565b6000808211610cb7576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381610cc057fe5b049392505050565b600082821115610d1f576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600082820183811015610c58576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b3390565b600060208284031215610d94578081fd5b8135610c58816110c7565b600060208284031215610db0578081fd5b8151610c58816110c7565b60008060408385031215610dcd578081fd5b8235610dd8816110c7565b946020939093013593505050565b600080600060608486031215610dfa578081fd5b8335610e05816110c7565b95602085013595506040909401359392505050565b600060208284031215610e2b578081fd5b81518015158114610c58578182fd5b600060208284031215610e4b578081fd5b5051919050565b6000815180845260208085019450808401835b83811015610e8157815187529582019590820190600101610e65565b509495945050505050565b15159052565b60008151808452815b81811015610eb757602081850181015186830182015201610e9b565b81811115610ec85782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6000858252602060018060a01b0380871682850152808616604085015260806060850152610100840185516080808701528181518084526101208801915085830193508692505b80831015610f9757835185168252928501926001929092019190850190610f75565b50848801519450607f199350838782030160a0880152610fb78186610e52565b94505050506040850151818584030160c0860152610fd58382610e92565b925050506060840151610feb60e0850182610e8c565b509695505050505050565b600060ff85168252606060208301526110126060830185610e52565b9050826040830152949350505050565b6020808252818101527f5061796d656e7453706c69747465723a207472616e73666572206661696c6564604082015260600190565b6020808252601690820152755061796d656e7453706c69747465723a20217261746560501b604082015260600190565b60208082526017908201527f5061796d656e7453706c69747465723a202161646d696e000000000000000000604082015260600190565b90815260200190565b6001600160a01b03811681146110dc57600080fd5b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220eba06cbcd528d8589e50089c90e52f7dc3fa5e7165ae1e40435ece99c19f65c564736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061009e5760003560e01c80637b103999116100665780637b10399914610111578063a6f19c8414610119578063a8734f0b14610121578063a91ee0dc14610134578063fbfa77cf146101475761009e565b80630efe6a8b146100a35780632bdb7097146100b85780633013ce29146100cb578063546af3c3146100e957806370a08231146100f1575b600080fd5b6100b66100b1366004610de6565b61014f565b005b6100b66100c6366004610dbb565b610a46565b6100d3610a9c565b6040516100e09190610edd565b60405180910390f35b6100d3610aab565b6101046100ff366004610d83565b610aba565b6040516100e091906110be565b6100d3610b64565b6100d3610b73565b61010461012f366004610d83565b610b82565b6100b6610142366004610d83565b610b94565b6100d3610bf0565b600260005414156101a7576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260009081556001600160a01b0384168152600760205260409020546101e95760405162461bcd60e51b81526004016101e090611057565b60405180910390fd5b6006546040516323b872dd60e01b81526001600160a01b03909116906323b872dd9061021d90869030908790600401610ef1565b602060405180830381600087803b15801561023757600080fd5b505af115801561024b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061026f9190610e1a565b61028b5760405162461bcd60e51b81526004016101e090611022565b60025460408051637a3d22ad60e11b815290516000926001600160a01b03169163f47a455a916004808301926020929190829003018186803b1580156102d057600080fd5b505afa1580156102e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103089190610e3a565b9050600061032261271061031c8685610bff565b90610c61565b6006546002546040805163cd44673560e01b815290519394506001600160a01b039283169363a9059cbb939092169163cd44673591600480820192602092909190829003018186803b15801561037757600080fd5b505afa15801561038b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103af9190610d9f565b836040518363ffffffff1660e01b81526004016103cd929190610f15565b602060405180830381600087803b1580156103e757600080fd5b505af11580156103fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041f9190610e1a565b61043b5760405162461bcd60e51b81526004016101e090611022565b604080516002808252606082018352600092602083019080368337505060065482519293506001600160a01b03169183915060009061047657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506000816001815181106104a557fe5b6001600160a01b039290921660209283029190910182015260408051600280825260608201835260009391929091830190803683370190505090506104ea8684610cc8565b816000815181106104f757fe5b60200260200101818152505060008160018151811061051257fe5b602090810291909101015260065460055482516001600160a01b039283169263095ea7b3921690849060009061054457fe5b60200260200101516040518363ffffffff1660e01b8152600401610569929190610f15565b602060405180830381600087803b15801561058357600080fd5b505af1158015610597573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105bb9190610e1a565b50600554600480546040805163038fff2d60e41b815290516001600160a01b039485169463b95cac28949316926338fff2d092808201926020929091829003018186803b15801561060b57600080fd5b505afa15801561061f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106439190610e3a565b303060405180608001604052808881526020018781526020016001888d60405160200161067293929190610ff6565b6040516020818303038152906040528152602001600015158152506040518563ffffffff1660e01b81526004016106ac9493929190610f2e565b600060405180830381600087803b1580156106c657600080fd5b505af11580156106da573d6000803e3d6000fd5b5050600480546040516370a0823160e01b8152600094506001600160a01b0390911692506370a082319161071091309101610edd565b60206040518083038186803b15801561072857600080fd5b505afa15801561073c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107609190610e3a565b6004805460035460405163095ea7b360e01b81529394506001600160a01b039182169363095ea7b3936107999390921691869101610f15565b602060405180830381600087803b1580156107b357600080fd5b505af11580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190610e1a565b5060035460405163b6b55f2560e01b81526001600160a01b039091169063b6b55f259061081c9084906004016110be565b600060405180830381600087803b15801561083657600080fd5b505af115801561084a573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190610881903090600401610edd565b60206040518083038186803b15801561089957600080fd5b505afa1580156108ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d19190610e3a565b600354600254604080516367906c3960e11b815290519394506001600160a01b039283169363a9059cbb939092169163cf20d87291600480820192602092909190829003018186803b15801561092657600080fd5b505afa15801561093a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095e9190610d9f565b836040518363ffffffff1660e01b815260040161097c929190610f15565b602060405180830381600087803b15801561099657600080fd5b505af11580156109aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ce9190610e1a565b6109ea5760405162461bcd60e51b81526004016101e090611022565b6001600160a01b038916600090815260086020526040902054610a0d9089610d25565b6001600160a01b0390991660009081526008602090815260408083209b909b556009905298892042905550506001909655505050505050565b6001546001600160a01b0316610a5a610d7f565b6001600160a01b031614610a805760405162461bcd60e51b81526004016101e090611087565b6001600160a01b03909116600090815260076020526040902055565b6006546001600160a01b031681565b6004546001600160a01b031681565b6001600160a01b038116600090815260076020908152604080832054600990925282205480610aee57600092505050610b5f565b426000610b0584610aff8486610cc8565b90610bff565b6001600160a01b038716600090815260086020526040902054909150811115610b35576000945050505050610b5f565b6001600160a01b038616600090815260086020526040902054610b589082610cc8565b9450505050505b919050565b6002546001600160a01b031681565b6003546001600160a01b031681565b60076020526000908152604090205481565b6001546001600160a01b0316610ba8610d7f565b6001600160a01b031614610bce5760405162461bcd60e51b81526004016101e090611087565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6005546001600160a01b031681565b600082610c0e57506000610c5b565b82820282848281610c1b57fe5b0414610c585760405162461bcd60e51b81526004018080602001828103825260218152602001806110e06021913960400191505060405180910390fd5b90505b92915050565b6000808211610cb7576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381610cc057fe5b049392505050565b600082821115610d1f576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600082820183811015610c58576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b3390565b600060208284031215610d94578081fd5b8135610c58816110c7565b600060208284031215610db0578081fd5b8151610c58816110c7565b60008060408385031215610dcd578081fd5b8235610dd8816110c7565b946020939093013593505050565b600080600060608486031215610dfa578081fd5b8335610e05816110c7565b95602085013595506040909401359392505050565b600060208284031215610e2b578081fd5b81518015158114610c58578182fd5b600060208284031215610e4b578081fd5b5051919050565b6000815180845260208085019450808401835b83811015610e8157815187529582019590820190600101610e65565b509495945050505050565b15159052565b60008151808452815b81811015610eb757602081850181015186830182015201610e9b565b81811115610ec85782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6000858252602060018060a01b0380871682850152808616604085015260806060850152610100840185516080808701528181518084526101208801915085830193508692505b80831015610f9757835185168252928501926001929092019190850190610f75565b50848801519450607f199350838782030160a0880152610fb78186610e52565b94505050506040850151818584030160c0860152610fd58382610e92565b925050506060840151610feb60e0850182610e8c565b509695505050505050565b600060ff85168252606060208301526110126060830185610e52565b9050826040830152949350505050565b6020808252818101527f5061796d656e7453706c69747465723a207472616e73666572206661696c6564604082015260600190565b6020808252601690820152755061796d656e7453706c69747465723a20217261746560501b604082015260600190565b60208082526017908201527f5061796d656e7453706c69747465723a202161646d696e000000000000000000604082015260600190565b90815260200190565b6001600160a01b03811681146110dc57600080fd5b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220eba06cbcd528d8589e50089c90e52f7dc3fa5e7165ae1e40435ece99c19f65c564736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/THXRegistry.json b/apps/api/src/app/hardhat/export/THXRegistry.json new file mode 100644 index 000000000..c91fd4daa --- /dev/null +++ b/apps/api/src/app/hardhat/export/THXRegistry.json @@ -0,0 +1,180 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "THXRegistry", + "sourceName": "contracts/utils/THXRegistry.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_paymentToken", + "type": "address" + }, + { + "internalType": "address", + "name": "_payee", + "type": "address" + }, + { + "internalType": "address", + "name": "_rewardDistributor", + "type": "address" + }, + { + "internalType": "address", + "name": "_gauge", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "gauge", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGauge", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPayee", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPaymentToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPayoutRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardDistributor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payee", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paymentToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rewardDistributor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rate", + "type": "uint256" + } + ], + "name": "setPayoutRate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506040516103683803806103688339818101604052608081101561003357600080fd5b50805160208201516040830151606090930151600080546001600160a01b039485166001600160a01b03199182161790915560018054938516938216939093179092556003805494841694831694909417909355600480549290931691161790556102c5806100a36000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063ae90b21311610071578063ae90b2131461011b578063cd44673514610123578063cf20d8721461012b578063d41c3a6514610133578063f2803f031461013b578063f47a455a14610143576100a9565b80632b4353f2146100ae5780633013ce29146100c857806348eaee66146100ec578063a6f19c841461010b578063acc2166a14610113575b600080fd5b6100b661014b565b60408051918252519081900360200190f35b6100d0610151565b604080516001600160a01b039092168252519081900360200190f35b6101096004803603602081101561010257600080fd5b5035610160565b005b6100d06101fb565b6100d061020a565b6100d0610219565b6100d0610228565b6100d0610237565b6100d0610246565b6100d0610255565b6100b6610264565b60025481565b6000546001600160a01b031681565b6001546001600160a01b031633146101b5576040805162461bcd60e51b815260206004820152601360248201527254485852656769737472793a2021706179656560681b604482015290519081900360640190fd5b6127108111156101f65760405162461bcd60e51b815260040180806020018281038252602581526020018061026b6025913960400191505060405180910390fd5b600255565b6004546001600160a01b031681565b6003546001600160a01b031681565b6001546001600160a01b031681565b6001546001600160a01b031690565b6003546001600160a01b031690565b6000546001600160a01b031690565b6004546001600160a01b031690565b6002549056fe54485852656769737472793a207061796f757452617465206f7574206f6620626f756e6473a26469706673582212209766fd3b31b5070d7ebcdb80534a3fdd12da7399c3e94c56888c415f0e1ef47064736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063ae90b21311610071578063ae90b2131461011b578063cd44673514610123578063cf20d8721461012b578063d41c3a6514610133578063f2803f031461013b578063f47a455a14610143576100a9565b80632b4353f2146100ae5780633013ce29146100c857806348eaee66146100ec578063a6f19c841461010b578063acc2166a14610113575b600080fd5b6100b661014b565b60408051918252519081900360200190f35b6100d0610151565b604080516001600160a01b039092168252519081900360200190f35b6101096004803603602081101561010257600080fd5b5035610160565b005b6100d06101fb565b6100d061020a565b6100d0610219565b6100d0610228565b6100d0610237565b6100d0610246565b6100d0610255565b6100b6610264565b60025481565b6000546001600160a01b031681565b6001546001600160a01b031633146101b5576040805162461bcd60e51b815260206004820152601360248201527254485852656769737472793a2021706179656560681b604482015290519081900360640190fd5b6127108111156101f65760405162461bcd60e51b815260040180806020018281038252602581526020018061026b6025913960400191505060405180910390fd5b600255565b6004546001600160a01b031681565b6003546001600160a01b031681565b6001546001600160a01b031681565b6001546001600160a01b031690565b6003546001600160a01b031690565b6000546001600160a01b031690565b6004546001600160a01b031690565b6002549056fe54485852656769737472793a207061796f757452617465206f7574206f6620626f756e6473a26469706673582212209766fd3b31b5070d7ebcdb80534a3fdd12da7399c3e94c56888c415f0e1ef47064736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/TestToken.json b/apps/api/src/app/hardhat/export/TestToken.json new file mode 100644 index 000000000..0ea496224 --- /dev/null +++ b/apps/api/src/app/hardhat/export/TestToken.json @@ -0,0 +1,304 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestToken", + "sourceName": "contracts/mock/Token.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5060405180604001604052806006815260200165546f6b656e3160d01b8152506040518060400160405280600681526020016553796d626c3160d01b815250816003908161005e9190610112565b50600461006b8282610112565b5050506101d1565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061009d57607f821691505b6020821081036100bd57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561010d57600081815260208120601f850160051c810160208610156100ea5750805b601f850160051c820191505b81811015610109578281556001016100f6565b5050505b505050565b81516001600160401b0381111561012b5761012b610073565b61013f816101398454610089565b846100c3565b602080601f831160018114610174576000841561015c5750858301515b600019600386901b1c1916600185901b178555610109565b600085815260208120601f198616915b828110156101a357888601518255948401946001909101908401610184565b50858210156101c15787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61093f806101e06000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c806340c10f191161007157806340c10f191461014157806370a082311461015657806395d89b411461017f578063a457c2d714610187578063a9059cbb1461019a578063dd62ed3e146101ad57600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461010c578063313ce5671461011f578063395093511461012e575b600080fd5b6100c16101c0565b6040516100ce9190610789565b60405180910390f35b6100ea6100e53660046107f3565b610252565b60405190151581526020016100ce565b6002545b6040519081526020016100ce565b6100ea61011a36600461081d565b61026c565b604051601281526020016100ce565b6100ea61013c3660046107f3565b610290565b61015461014f3660046107f3565b6102b2565b005b6100fe610164366004610859565b6001600160a01b031660009081526020819052604090205490565b6100c16102c0565b6100ea6101953660046107f3565b6102cf565b6100ea6101a83660046107f3565b61034f565b6100fe6101bb36600461087b565b61035d565b6060600380546101cf906108ae565b80601f01602080910402602001604051908101604052809291908181526020018280546101fb906108ae565b80156102485780601f1061021d57610100808354040283529160200191610248565b820191906000526020600020905b81548152906001019060200180831161022b57829003601f168201915b5050505050905090565b600033610260818585610388565b60019150505b92915050565b60003361027a8582856104ac565b610285858585610526565b506001949350505050565b6000336102608185856102a3838361035d565b6102ad91906108e8565b610388565b6102bc82826106ca565b5050565b6060600480546101cf906108ae565b600033816102dd828661035d565b9050838110156103425760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b6102858286868403610388565b600033610260818585610526565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103ea5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610339565b6001600160a01b03821661044b5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610339565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006104b8848461035d565b9050600019811461052057818110156105135760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610339565b6105208484848403610388565b50505050565b6001600160a01b03831661058a5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610339565b6001600160a01b0382166105ec5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610339565b6001600160a01b038316600090815260208190526040902054818110156106645760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610339565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610520565b6001600160a01b0382166107205760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610339565b806002600082825461073291906108e8565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b818110156107b65785810183015185820160400152820161079a565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146107ee57600080fd5b919050565b6000806040838503121561080657600080fd5b61080f836107d7565b946020939093013593505050565b60008060006060848603121561083257600080fd5b61083b846107d7565b9250610849602085016107d7565b9150604084013590509250925092565b60006020828403121561086b57600080fd5b610874826107d7565b9392505050565b6000806040838503121561088e57600080fd5b610897836107d7565b91506108a5602084016107d7565b90509250929050565b600181811c908216806108c257607f821691505b6020821081036108e257634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561026657634e487b7160e01b600052601160045260246000fdfea264697066735822122061db66c492e91059f7307777f5295b8df5b39f9055c2a3cb521f545cfe1b69c764736f6c63430008120033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100b45760003560e01c806340c10f191161007157806340c10f191461014157806370a082311461015657806395d89b411461017f578063a457c2d714610187578063a9059cbb1461019a578063dd62ed3e146101ad57600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461010c578063313ce5671461011f578063395093511461012e575b600080fd5b6100c16101c0565b6040516100ce9190610789565b60405180910390f35b6100ea6100e53660046107f3565b610252565b60405190151581526020016100ce565b6002545b6040519081526020016100ce565b6100ea61011a36600461081d565b61026c565b604051601281526020016100ce565b6100ea61013c3660046107f3565b610290565b61015461014f3660046107f3565b6102b2565b005b6100fe610164366004610859565b6001600160a01b031660009081526020819052604090205490565b6100c16102c0565b6100ea6101953660046107f3565b6102cf565b6100ea6101a83660046107f3565b61034f565b6100fe6101bb36600461087b565b61035d565b6060600380546101cf906108ae565b80601f01602080910402602001604051908101604052809291908181526020018280546101fb906108ae565b80156102485780601f1061021d57610100808354040283529160200191610248565b820191906000526020600020905b81548152906001019060200180831161022b57829003601f168201915b5050505050905090565b600033610260818585610388565b60019150505b92915050565b60003361027a8582856104ac565b610285858585610526565b506001949350505050565b6000336102608185856102a3838361035d565b6102ad91906108e8565b610388565b6102bc82826106ca565b5050565b6060600480546101cf906108ae565b600033816102dd828661035d565b9050838110156103425760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b6102858286868403610388565b600033610260818585610526565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103ea5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610339565b6001600160a01b03821661044b5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610339565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006104b8848461035d565b9050600019811461052057818110156105135760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610339565b6105208484848403610388565b50505050565b6001600160a01b03831661058a5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610339565b6001600160a01b0382166105ec5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610339565b6001600160a01b038316600090815260208190526040902054818110156106645760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610339565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610520565b6001600160a01b0382166107205760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610339565b806002600082825461073291906108e8565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b818110156107b65785810183015185820160400152820161079a565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146107ee57600080fd5b919050565b6000806040838503121561080657600080fd5b61080f836107d7565b946020939093013593505050565b60008060006060848603121561083257600080fd5b61083b846107d7565b9250610849602085016107d7565b9150604084013590509250925092565b60006020828403121561086b57600080fd5b610874826107d7565b9392505050565b6000806040838503121561088e57600080fd5b610897836107d7565b91506108a5602084016107d7565b90509250929050565b600181811c908216806108c257607f821691505b6020821081036108e257634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561026657634e487b7160e01b600052601160045260246000fdfea264697066735822122061db66c492e91059f7307777f5295b8df5b39f9055c2a3cb521f545cfe1b69c764736f6c63430008120033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/USDC.json b/apps/api/src/app/hardhat/export/USDC.json new file mode 100644 index 000000000..56669e317 --- /dev/null +++ b/apps/api/src/app/hardhat/export/USDC.json @@ -0,0 +1,297 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "USDC", + "sourceName": "contracts/mock/USDC.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000d1938038062000d19833981810160405260408110156200003757600080fd5b508051602091820151604080518082018252600e81526d55534420436f696e2028506f532960901b81860190815282518084019093526006835265555344432e6560d01b9583019590955280519394929390926200009991600391906200026b565b508051620000af9060049060208401906200026b565b50506005805460ff1916601217905550620000cb6006620000df565b620000d78282620000f5565b505062000317565b6005805460ff191660ff92909216919091179055565b6001600160a01b03821662000151576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6200015f6000838362000204565b6200017b816002546200020960201b620005731790919060201c565b6002556001600160a01b03821660009081526020818152604090912054620001ae9183906200057362000209821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b60008282018381101562000264576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002a35760008555620002ee565b82601f10620002be57805160ff1916838001178555620002ee565b82800160010185558215620002ee579182015b82811115620002ee578251825591602001919060010190620002d1565b50620002fc92915062000300565b5090565b5b80821115620002fc576000815560010162000301565b6109f280620003276000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103f9565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610402565b6101736004803603602081101561021b57600080fd5b50356001600160a01b0316610450565b6100b661046b565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104cc565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610534565b610173600480360360408110156102a157600080fd5b506001600160a01b0381358116916020013516610548565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c6105d4565b84846105d8565b50600192915050565b60025490565b600061037f8484846106c4565b6103ef8461038b6105d4565b6103ea85604051806060016040528060288152602001610927602891396001600160a01b038a166000908152600160205260408120906103c96105d4565b6001600160a01b03168152602081019190915260400160002054919061081f565b6105d8565b5060019392505050565b60055460ff1690565b600061036361040f6105d4565b846103ea85600160006104206105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610573565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104d96105d4565b846103ea8560405180606001604052806025815260200161099860259139600160006105036105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061081f565b60006103636105416105d4565b84846106c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6000828201838110156105cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b03831661061d5760405162461bcd60e51b81526004018080602001828103825260248152602001806109746024913960400191505060405180910390fd5b6001600160a01b0382166106625760405162461bcd60e51b81526004018080602001828103825260228152602001806108df6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107095760405162461bcd60e51b815260040180806020018281038252602581526020018061094f6025913960400191505060405180910390fd5b6001600160a01b03821661074e5760405162461bcd60e51b81526004018080602001828103825260238152602001806108bc6023913960400191505060405180910390fd5b6107598383836108b6565b61079681604051806060016040528060268152602001610901602691396001600160a01b038616600090815260208190526040902054919061081f565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c59082610573565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108ae5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087357818101518382015260200161085b565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212201f8cdee0bd0e245ffd8ccb83c246c0b366118d610a54acf6eeed476dca2dfa1964736f6c63430007060033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103f9565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610402565b6101736004803603602081101561021b57600080fd5b50356001600160a01b0316610450565b6100b661046b565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104cc565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610534565b610173600480360360408110156102a157600080fd5b506001600160a01b0381358116916020013516610548565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c6105d4565b84846105d8565b50600192915050565b60025490565b600061037f8484846106c4565b6103ef8461038b6105d4565b6103ea85604051806060016040528060288152602001610927602891396001600160a01b038a166000908152600160205260408120906103c96105d4565b6001600160a01b03168152602081019190915260400160002054919061081f565b6105d8565b5060019392505050565b60055460ff1690565b600061036361040f6105d4565b846103ea85600160006104206105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610573565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104d96105d4565b846103ea8560405180606001604052806025815260200161099860259139600160006105036105d4565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919061081f565b60006103636105416105d4565b84846106c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6000828201838110156105cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3390565b6001600160a01b03831661061d5760405162461bcd60e51b81526004018080602001828103825260248152602001806109746024913960400191505060405180910390fd5b6001600160a01b0382166106625760405162461bcd60e51b81526004018080602001828103825260228152602001806108df6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107095760405162461bcd60e51b815260040180806020018281038252602581526020018061094f6025913960400191505060405180910390fd5b6001600160a01b03821661074e5760405162461bcd60e51b81526004018080602001828103825260238152602001806108bc6023913960400191505060405180910390fd5b6107598383836108b6565b61079681604051806060016040528060268152602001610901602691396001600160a01b038616600090815260208190526040902054919061081f565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c59082610573565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108ae5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087357818101518382015260200161085b565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212201f8cdee0bd0e245ffd8ccb83c246c0b366118d610a54acf6eeed476dca2dfa1964736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/export/VotingEscrow.json b/apps/api/src/app/hardhat/export/VotingEscrow.json new file mode 100644 index 000000000..4f72b7728 --- /dev/null +++ b/apps/api/src/app/hardhat/export/VotingEscrow.json @@ -0,0 +1,1028 @@ +{ + "_format": "hh-vyper-artifact-1", + "contractName": "VotingEscrow", + "sourceName": "contracts/VotingEscrow.vy", + "abi": [ + { + "name": "CommitOwnership", + "inputs": [ + { + "name": "admin", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "ApplyOwnership", + "inputs": [ + { + "name": "admin", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "EarlyUnlock", + "inputs": [ + { + "name": "status", + "type": "bool", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "PenaltySpeed", + "inputs": [ + { + "name": "penalty_k", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "PenaltyTreasury", + "inputs": [ + { + "name": "penalty_treasury", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "TotalUnlock", + "inputs": [ + { + "name": "status", + "type": "bool", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "RewardReceiver", + "inputs": [ + { + "name": "newReceiver", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "Deposit", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true + }, + { + "name": "value", + "type": "uint256", + "indexed": false + }, + { + "name": "locktime", + "type": "uint256", + "indexed": true + }, + { + "name": "type", + "type": "int128", + "indexed": false + }, + { + "name": "ts", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "Withdraw", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true + }, + { + "name": "value", + "type": "uint256", + "indexed": false + }, + { + "name": "ts", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "WithdrawEarly", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true + }, + { + "name": "penalty", + "type": "uint256", + "indexed": false + }, + { + "name": "time_left", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "Supply", + "inputs": [ + { + "name": "prevSupply", + "type": "uint256", + "indexed": false + }, + { + "name": "supply", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "_token_addr", + "type": "address" + }, + { + "name": "_name", + "type": "string" + }, + { + "name": "_symbol", + "type": "string" + }, + { + "name": "_admin_addr", + "type": "address" + }, + { + "name": "_admin_unlock_all", + "type": "address" + }, + { + "name": "_admin_early_unlock", + "type": "address" + }, + { + "name": "_max_time", + "type": "uint256" + }, + { + "name": "_balToken", + "type": "address" + }, + { + "name": "_balMinter", + "type": "address" + }, + { + "name": "_rewardReceiver", + "type": "address" + }, + { + "name": "_rewardReceiverChangeable", + "type": "bool" + }, + { + "name": "_rewardDistributor", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "token", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "commit_transfer_ownership", + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "apply_transfer_ownership", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "commit_smart_wallet_checker", + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "apply_smart_wallet_checker", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_early_unlock", + "inputs": [ + { + "name": "_early_unlock", + "type": "bool" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_early_unlock_penalty_speed", + "inputs": [ + { + "name": "_penalty_k", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_penalty_treasury", + "inputs": [ + { + "name": "_penalty_treasury", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_all_unlock", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_last_user_slope", + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "int128" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "user_point_history__ts", + "inputs": [ + { + "name": "_addr", + "type": "address" + }, + { + "name": "_idx", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "locked__end", + "inputs": [ + { + "name": "_addr", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "checkpoint", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deposit_for", + "inputs": [ + { + "name": "_addr", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "create_lock", + "inputs": [ + { + "name": "_value", + "type": "uint256" + }, + { + "name": "_unlock_time", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "increase_amount", + "inputs": [ + { + "name": "_value", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "increase_unlock_time", + "inputs": [ + { + "name": "_unlock_time", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "withdraw", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "withdraw_early", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "addr", + "type": "address" + }, + { + "name": "_t", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balanceOfAt", + "inputs": [ + { + "name": "addr", + "type": "address" + }, + { + "name": "_block", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "totalSupply", + "inputs": [ + { + "name": "t", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "totalSupplyAt", + "inputs": [ + { + "name": "_block", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "claimExternalRewards", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "changeRewardReceiver", + "inputs": [ + { + "name": "newReceiver", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "MAXTIME", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "TOKEN", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "supply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "locked", + "inputs": [ + { + "name": "arg0", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "components": [ + { + "name": "amount", + "type": "int128" + }, + { + "name": "end", + "type": "uint256" + } + ] + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "epoch", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "point_history", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "components": [ + { + "name": "bias", + "type": "int128" + }, + { + "name": "slope", + "type": "int128" + }, + { + "name": "ts", + "type": "uint256" + }, + { + "name": "blk", + "type": "uint256" + } + ] + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "user_point_history", + "inputs": [ + { + "name": "arg0", + "type": "address" + }, + { + "name": "arg1", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "components": [ + { + "name": "bias", + "type": "int128" + }, + { + "name": "slope", + "type": "int128" + }, + { + "name": "ts", + "type": "uint256" + }, + { + "name": "blk", + "type": "uint256" + } + ] + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "user_point_epoch", + "inputs": [ + { + "name": "arg0", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "slope_changes", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "int128" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_smart_wallet_checker", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "smart_wallet_checker", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "admin", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "admin_unlock_all", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "admin_early_unlock", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_admin", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "is_initialized", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "early_unlock", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "penalty_k", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "prev_penalty_k", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "penalty_upd_ts", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "penalty_treasury", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balMinter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balToken", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "rewardReceiver", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "rewardReceiverChangeable", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "rewardDistributor", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "all_unlock", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + } + ], + "bytecode": "0x613bea61001161000039613bea610000f36003361161000c57612d19565b60003560e01c34613bd857639bf6abf3811861047c576101c43610613bd8576004358060a01c613bd8576040526024356004016040813511613bd8578035806060526020820181816080375050506044356004016020813511613bd85780358060c05260208201803560e0525050506064358060a01c613bd857610100526084358060a01c613bd8576101205260a4358060a01c613bd8576101405260e4358060a01c613bd85761016052610104358060a01c613bd85761018052610124358060a01c613bd8576101a052610144358060011c613bd8576101c052610164358060a01c613bd8576101e0526c050c783eb9b5c84000000000155415610171576009610200527f6f6e6c79206f6e636500000000000000000000000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b60016c050c783eb9b5c840000000001555610100516101f0576006610200527f21656d70747900000000000000000000000000000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b610100516c050c783eb9b5c840000000001155600a6c050c783eb9b5c840000000001755600a6c050c783eb9b5c840000000001855426c050c783eb9b5c840000000001955610100516c050c783eb9b5c840000000001a5560405160025543600f5542600e5560405163313ce567610220526020610220600461023c845afa61027e573d600060003e3d6000fd5b60203d10613bd8576102209050516102005260066102005110156102a35760006102ac565b60ff6102005111155b610316576009610220527f21646563696d616c7300000000000000000000000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b60605180600355600081601f0160051c60028111613bd857801561034e57905b8060051b608001518160040155600101818118610336575b50505060c0518060065560e051600755506102005160085562093a8060c435101561037a576000610385565b63095f6a0060c43511155b6103ef576008610220527f216d61786c6f636b0000000000000000000000000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b60c435600155610120516c050c783eb9b5c840000000001255610140516c050c783eb9b5c840000000001355610160516c050c783eb9b5c840000000001c55610180516c050c783eb9b5c840000000001b556101a0516c050c783eb9b5c840000000001d556101c0516c050c783eb9b5c840000000001e556101e0516c050c783eb9b5c840000000001f55005b63fc0c546a811861049b5760043610613bd85760025460405260206040f35b6306fdde0381186105205760043610613bd8576020806040528060400160035480825260208201600082601f0160051c60028111613bd85780156104f257905b80600401548160051b8401526001018181186104db575b505050508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6395d89b4181186105785760043610613bd8576020806040528060400160065480825260208201600754815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b63313ce56781186105975760043610613bd85760085460405260206040f35b636b441a40811861060d5760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c8400000000011543318613bd8576040516c050c783eb9b5c8400000000014557f2f56810a6bf40af059b96d3aea4db54081f378029a518390491093a7b67032e960405160605260206060a1005b636a1c05ae811861068f5760043610613bd8576c050c783eb9b5c8400000000011543318613bd8576c050c783eb9b5c84000000000145460405260405115613bd8576040516c050c783eb9b5c8400000000011557febee2d5739011062cb4f14113f3b36bf0ffe3da5c0568f64189d1012a118910560405160605260206060a1005b6357f901e281186106d95760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c8400000000011543318613bd8576040516c050c783eb9b5c840000000000f55005b638e5b490f81186107215760043610613bd8576c050c783eb9b5c8400000000011543318613bd8576c050c783eb9b5c840000000000f546c050c783eb9b5c840000000001055005b6355a3323581186108695760243610613bd8576004358060011c613bd8576040526c050c783eb9b5c8400000000013543318156107b55760066060527f2161646d696e000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c050c783eb9b5c840000000001654604051186108295760076060527f616c72656164790000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c050c783eb9b5c8400000000016557f66515f71c349ef0ad8c6981cedaa58746200512e6e12754c5ac5cc701d5cf41860405160605260206060a1005b63655317ae8118610a445760243610613bd8576c050c783eb9b5c8400000000013543318156108ef5760066040527f2161646d696e000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b603260043511156109575760026040527f216b00000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6c050c783eb9b5c840000000001954603c8101818110613bd857905042116109d65760056040527f6561726c7900000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6c050c783eb9b5c8400000000017546c050c783eb9b5c8400000000018556004356c050c783eb9b5c840000000001755426c050c783eb9b5c8400000000019557f9c04360e3c3de1eeca25bbd4a21cdc22c4c192f4b91f91d2a0c59dcd042f8ba860043560405260206040a1005b63af3097af8118610b7c5760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000001354331815610ad85760066060527f2161646d696e000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b604051610b3c5760056060527f217a65726f00000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c050c783eb9b5c840000000001a557ff51c492eafc918620c2d49b196e6a4e0cac71709d348224a8e7fc231ee973e5960405160605260206060a1005b6366e01ca98118610c405760043610613bd8576c050c783eb9b5c840000000001254331815610c025760066040527f2161646d696e000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b60016c050c783eb9b5c8400000000020557f7ca88488f569b55d1fb073429f75839e505182c1f6d26e5caed22e9828df430c600160405260206040a1005b637c74a1748118610cc25760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000d6040516020526000526040600020546060526c050c783eb9b5c840000000000c6040516020526000526040600020606051633b9ac9ff8111613bd85760021b810190506001810190505460805260206080f35b63da020a188118610d245760443610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000c6040516020526000526040600020602435633b9ac9ff8111613bd85760021b810190506002810190505460605260206060f35b63adc635898118610d655760243610613bd8576004358060a01c613bd857604052600a60405160205260005260406000206001810190505460605260206060f35b63c2c4c5c18118610d885760043610613bd85760a036604037610d86612e17565b005b633a46273e8118610f345760443610613bd8576004358060a01c613bd8576105e052600054600214613bd8576002600055600a6105e05160205260005260406000208054610600526001810154610620525060243515613bd8576001610600511215610e54576016610640527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b426106205111610ee9576024610640527f43616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468610660527f64726177000000000000000000000000000000000000000000000000000000006106805261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b6105e0516103e05260243561040052600061042052600a6105e051602052600052604060002080546104405260018101546104605250600061048052610f2d61352a565b6003600055005b6365fc3873811861116a5760443610613bd857600054600214613bd857600260005533604052610f62612d1f565b60243562093a808104905062093a8081028162093a80820418613bd85790506105e052600a3360205260005260406000208054610600526001810154610620525060043515613bd857610600511561101a576019610640527f5769746864726177206f6c6420746f6b656e73206669727374000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b426105e051116110af576026610640527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e2074686520610660527f66757475726500000000000000000000000000000000000000000000000000006106805261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b42600154808201828110613bd857905090506105e0511115611131576014610640527f566f74696e67206c6f636b20746f6f206c6f6e670000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b336103e052600435610400526105e051610420526106005161044052610620516104605260016104805261116361352a565b6003600055005b634957677c81186112fc5760243610613bd857600054600214613bd857600260005533604052611198612d1f565b600a33602052600052604060002080546105e0526001810154610600525060043515613bd85760016105e0511215611230576016610620527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006106405261062050610620518061064001601f826000031636823750506308c379a06105e052602061060052601f19601f6106205101166044016105fcfd5b4261060051116112c5576024610620527f43616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468610640527f64726177000000000000000000000000000000000000000000000000000000006106605261062050610620518061064001601f826000031636823750506308c379a06105e052602061060052601f19601f6106205101166044016105fcfd5b336103e052600435610400526000610420526105e0516104405261060051610460526002610480526112f561352a565b6003600055005b63eff7a612811861157a5760243610613bd857600054600214613bd85760026000553360405261132a612d1f565b600a33602052600052604060002080546105e0526001810154610600525060043562093a808104905062093a8081028162093a80820418613bd8579050610620524261060051116113db57600c610640527f4c6f636b206578706972656400000000000000000000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b60016105e051121561144d576011610640527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b6106005161062051116114c057601f610640527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b42600154808201828110613bd85790509050610620511115611542576014610640527f566f74696e67206c6f636b20746f6f206c6f6e670000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b336103e05260006104005261062051610420526105e05161044052610600516104605260036104805261157361352a565b6003600055005b633ccfd60b81186117cf5760043610613bd857600054600214613bd8576002600055600a33602052600052604060002080546103e05260018101546104005250610400514210156115d9576c050c783eb9b5c8400000000020546115dc565b60015b611646576017610420527f6c6f636b2021657870697265206f722021756e6c6f636b0000000000000000006104405261042050610420518061044001601f826000031636823750506308c379a06103e052602061040052601f19601f6104205101166044016103fcfd5b6103e05160008112613bd857610420526103e05161044052610400516104605260006104005260006103e052600a3360205260005260406000206103e051815561040051600182015550600954610480526104805161042051808203828111613bd857905090506009553360405261044051606052610460516080526103e05160a0526104005160c0526116d8612e17565b60025463a9059cbb6104a052336104c052610420516104e05260206104a060446104bc6000855af161170f573d600060003e3d6000fd5b3d61172657803b15613bd85760016105005261173f565b60203d10613bd8576104a0518060011c613bd857610500525b61050090505115613bd857337ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610420516104a052426104c05260406104a0a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c610480516104a0526104805161042051808203828111613bd857905090506104c05260406104a0a16003600055005b638239f0648118611c6e5760043610613bd857600054600214613bd857600260005560016c050c783eb9b5c840000000001654181561186e57600d6103e0527f216561726c7920756e6c6f636b00000000000000000000000000000000000000610400526103e0506103e0518061040001601f826000031636823750506308c379a06103a05260206103c052601f19601f6103e05101166044016103bcfd5b600a33602052600052604060002080546103e052600181015461040052506104005142106118fc57600c610420527f6c6f636b206578706972656400000000000000000000000000000000000000006104405261042050610420518061044001601f826000031636823750506308c379a06103e052602061040052601f19601f6104205101166044016103fcfd5b6103e05160008112613bd857610420526104005142808203828111613bd85790509050610440526000610460526c050c783eb9b5c840000000001954603c8101818110613bd85790504211611963576c050c783eb9b5c84000000000185461046052611977565b6c050c783eb9b5c840000000001754610460525b61044051670de0b6b3a7640000810281670de0b6b3a7640000820418613bd85790506001548015613bd8578082049050905061046051808202811583838304141715613bd85790509050610480526104205161048051808202811583838304141715613bd85790509050670de0b6b3a764000081049050600a810490506104a052610420516104a0511115611a0f57610420516104a0525b610420516104a051808203828111613bd857905090506104c0526103e0516104e052610400516105005260006104005260006103e052600a3360205260005260406000206103e051815561040051600182015550600954610520526105205161042051808203828111613bd85790509050600955336040526104e051606052610500516080526103e05160a0526104005160c052611aab612e17565b6104a05115611b355760025463a9059cbb610540526c050c783eb9b5c840000000001a54610560526104a051610580526020610540604461055c6000855af1611af9573d600060003e3d6000fd5b3d611b1057803b15613bd85760016105a052611b29565b60203d10613bd857610540518060011c613bd8576105a0525b6105a090505115613bd8575b6104c05115611bb15760025463a9059cbb6105405233610560526104c051610580526020610540604461055c6000855af1611b75573d600060003e3d6000fd5b3d611b8c57803b15613bd85760016105a052611ba5565b60203d10613bd857610540518060011c613bd8576105a0525b6105a090505115613bd8575b337ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610420516105405242610560526040610540a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c61052051610540526105205161042051808203828111613bd85790509050610560526040610540a1337ff11a1a5a36ad286d4f77c166e8d87e5bfe87d1e74f9c46654ef21bd811c6784f6104a0516105405261044051610560526040610540a26003600055005b6370a082318118611c8b5760243610613bd8574261014052611ca5565b62fdd58e8118611e345760443610613bd857602435610140525b6004358060a01c613bd85761012052600061016052426101405118611ceb576c050c783eb9b5c840000000000d6101205160205260005260406000205461016052611d2e565b61012051604052610140516060526c050c783eb9b5c840000000000d61012051602052600052604060002054608052611d256101806139a2565b61018051610160525b61016051611d4a576000610180526020610180611e3256611e32565b6c050c783eb9b5c840000000000c61012051602052600052604060002061016051633b9ac9ff8111613bd85760021b8101905080546101805260018101546101a05260028101546101c05260038101546101e05250610180516101a051610140516101c051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd85790509050610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113611e1c576000610180525b6101805160008112613bd8576102005260206102005bf35b634ee2cd7e811861210f5760443610613bd8576004358060a01c613bd857610120524360243511613bd857610120516040526024356060526c050c783eb9b5c840000000000d61012051602052600052604060002054608052611e986101606138da565b61016051610140526c050c783eb9b5c840000000000c61012051602052600052604060002061014051633b9ac9ff8111613bd85760021b8101905080546101605260018101546101805260028101546101a05260038101546101c05250600b546101e0526024356040526101e051606052611f1461022061377a565b6102205161020052610200516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c01805461022052600181015461024052600281015461026052600381015461028052506040366102a0376101e0516102005110611fa3574361028051808203828111613bd857905090506102a0524261026051808203828111613bd857905090506102c052612025565b6102005160018101818110613bd85790506c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0180546102e052600181015461030052600281015461032052600381015461034052506103405161028051808203828111613bd857905090506102a0526103205161026051808203828111613bd857905090506102c0525b610260516102e0526102a0511561208b576102e0516102c05160243561028051808203828111613bd85790509050808202811583838304141715613bd857905090506102a0518015613bd85780820490509050808201828110613bd857905090506102e0525b61016051610180516102e0516101a051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd857905090506101605260006101605112156120f757600061030052602061030061210d5661210d565b6101605160008112613bd8576103005260206103005bf35b6318160ddd811861212c5760043610613bd857426101c052612147565b63bd85b03981186122165760243610613bd8576004356101c0525b60006101e052426101c0511861216357600b546101e052612184565b6101c051604052600b5460605261217b61020061382a565b610200516101e0525b6101e0516121a057600061020052602061020061221456612214565b6101e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c018054610200526001810154610220526002810154610240526003810154610260525060206102005160405261022051606052610240516080526102605160a0526101c05160c052612210610280613a6a565b6102805bf35b63981b24d081186124235760243610613bd8574360043511613bd857600b546101c0526004356040526101c05160605261225161020061377a565b610200516101e0526101e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c01805461020052600181015461022052600281015461024052600381015461026052506000610280526101c0516101e0511061231b574361026051146123dc5760043561026051808203828111613bd857905090504261024051808203828111613bd85790509050808202811583838304141715613bd857905090504361026051808203828111613bd857905090508015613bd85780820490509050610280526123dc565b6101e05160018101818110613bd85790506c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0180546102a05260018101546102c05260028101546102e052600381015461030052506103005161026051146123dc5760043561026051808203828111613bd857905090506102e05161024051808203828111613bd85790509050808202811583838304141715613bd857905090506103005161026051808203828111613bd857905090508015613bd85780820490509050610280525b60206102005160405261022051606052610240516080526102605160a0526102405161028051808201828110613bd8579050905060c05261241e6102a0613a6a565b6102a0f35b63db93cc7a811861265e5760043610613bd857600054600214613bd85760026000556c050c783eb9b5c840000000001b54636a627842604052600254606052602060406024605c6000855af161247e573d600060003e3d6000fd5b60203d10613bd857604050506c050c783eb9b5c840000000001c546370a0823160605230608052602060606024607c845afa6124bf573d600060003e3d6000fd5b60203d10613bd857606090505160405260405115612657576c050c783eb9b5c840000000001f546c050c783eb9b5c840000000001d54186125d4576c050c783eb9b5c840000000001c5463095ea7b36060526c050c783eb9b5c840000000001f5460805260405160a052602060606044607c6000855af1612545573d600060003e3d6000fd5b3d61255b57803b15613bd857600160c052612572565b60203d10613bd8576060518060011c613bd85760c0525b60c090505115613bd8576c050c783eb9b5c840000000001f5463338b5dea6060526c050c783eb9b5c840000000001c5460805260405160a052803b15613bd857600060606044607c6000855af16125ce573d600060003e3d6000fd5b50612657565b6c050c783eb9b5c840000000001c5463a9059cbb6060526c050c783eb9b5c840000000001d5460805260405160a052602060606044607c6000855af161261f573d600060003e3d6000fd5b3d61263557803b15613bd857600160c05261264c565b60203d10613bd8576060518060011c613bd85760c0525b60c090505115613bd8575b6003600055005b63cb8e090d81186128065760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c8400000000011543318156126f25760066060527f2161646d696e000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c050c783eb9b5c840000000001e5461276257600a6060527f21617661696c61626c650000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516127c65760066060527f21656d707479000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c050c783eb9b5c840000000001d557f454ccf21cdac2e344d64173597f5922657c25c5b1e6c3f733791637513d2063760405160605260206060a1005b63ee00ef3a81186128255760043610613bd85760015460405260206040f35b6382bfefc881186128445760043610613bd85760025460405260206040f35b63047fc9aa81186128635760043610613bd85760095460405260206040f35b63cbf9fe5f81186128a85760243610613bd8576004358060a01c613bd857604052600a6040516020526000526040600020805460605260018101546080525060406060f35b63900cf0cf81186128c75760043610613bd857600b5460405260206040f35b63d1febfb9811861291b5760243610613bd8576004356c01431e0fae6d7217ca9fffffff8111613bd85760021b600c01805460405260018101546060526002810154608052600381015460a0525060806040f35b6328d09d4781186129915760443610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000c6040516020526000526040600020602435633b9ac9ff8111613bd85760021b8101905080546060526001810154608052600281015460a052600381015460c0525060806060f35b63010ae75781186129d85760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000d60405160205260005260406000205460605260206060f35b63711974848118612a115760243610613bd8576c050c783eb9b5c840000000000e60043560205260005260406000205460405260206040f35b638ff36fd18118612a3c5760043610613bd8576c050c783eb9b5c840000000000f5460405260206040f35b637175d4f78118612a675760043610613bd8576c050c783eb9b5c84000000000105460405260206040f35b63f851a4408118612a925760043610613bd8576c050c783eb9b5c84000000000115460405260206040f35b63142614258118612abd5760043610613bd8576c050c783eb9b5c84000000000125460405260206040f35b6322cf35f58118612ae85760043610613bd8576c050c783eb9b5c84000000000135460405260206040f35b6317f7182a8118612b135760043610613bd8576c050c783eb9b5c84000000000145460405260206040f35b639a01873c8118612b3e5760043610613bd8576c050c783eb9b5c84000000000155460405260206040f35b63f68467278118612b695760043610613bd8576c050c783eb9b5c84000000000165460405260206040f35b63cd8c79aa8118612b945760043610613bd8576c050c783eb9b5c84000000000175460405260206040f35b63094cda238118612bbf5760043610613bd8576c050c783eb9b5c84000000000185460405260206040f35b63205ad4088118612bea5760043610613bd8576c050c783eb9b5c84000000000195460405260206040f35b635836ec3a8118612c155760043610613bd8576c050c783eb9b5c840000000001a5460405260206040f35b6373f43d6d8118612c405760043610613bd8576c050c783eb9b5c840000000001b5460405260206040f35b6338d546458118612c6b5760043610613bd8576c050c783eb9b5c840000000001c5460405260206040f35b631dac30b08118612c965760043610613bd8576c050c783eb9b5c840000000001d5460405260206040f35b63b027651a8118612cc15760043610613bd8576c050c783eb9b5c840000000001e5460405260206040f35b63acc2166a8118612cec5760043610613bd8576c050c783eb9b5c840000000001f5460405260206040f35b63b3d8f7e78118612d175760043610613bd8576c050c783eb9b5c84000000000205460405260206040f35b505b60006000fd5b3260405114612e15576c050c783eb9b5c84000000000105460605260605115612d945760605163c23697a860805260405160a052602060806024609c6000855af1612d6f573d600060003e3d6000fd5b60203d10613bd8576080518060011c613bd85760c05260c090505115612d9457612e15565b60256080527f536d61727420636f6e7472616374206465706f7369746f7273206e6f7420616c60a0527f6c6f77656400000000000000000000000000000000000000000000000000000060c0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b565b6101403660e037600b546102205260405115612f83574260805111612e3d576000612e45565b600160605112155b15612ea65760605160015480607f1c613bd8578015613bd85780820580600f0b8118613bd85790509050610100526101005160805142808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905060e0525b4260c05111612eb6576000612ebe565b600160a05112155b15612f205760a05160015480607f1c613bd8578015613bd85780820580600f0b8118613bd85790509050610180526101805160c05142808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd85790509050610160525b6c050c783eb9b5c840000000000e6080516020526000526040600020546101e05260c05115612f835760805160c05118612f61576101e05161020052612f83565b6c050c783eb9b5c840000000000e60c051602052600052604060002054610200525b604036610240374261028052436102a0526102205115612fde57610220516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0180546102405260018101546102605260028101546102805260038101546102a052505b610280516102c052610240516102e052610260516103005261028051610320526102a051610340526000610360526102805142111561306d57436102a051808203828111613bd85790509050670de0b6b3a7640000810281670de0b6b3a7640000820418613bd85790504261028051808203828111613bd857905090508015613bd85780820490509050610360525b6102c05162093a808104905062093a8081028162093a80820418613bd857905061038052600060ff905b806103a0526103805162093a808101818110613bd85790506103805260006103c0524261038051116130ea576c050c783eb9b5c840000000000e610380516020526000526040600020546103c0526130f0565b42610380525b6102405161026051610380516102c051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd8579050905061024052610260516103c05180820180600f0b8118613bd85790509050610260527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610240511361318a576000610240525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61026051136131bb576000610260525b610380516102c052610380516102805261034051610360516103805161032051808203828111613bd85790509050808202811583838304141715613bd85790509050670de0b6b3a764000081049050808201828110613bd857905090506102a0526102205160018101818110613bd85790506102205242610380511861324957436102a0526132955661328a565b610220516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c016102405181556102605160018201556102805160028201556102a0516003820155505b600101818118613097575b505061022051600b556040511561336b5761026051610180516101005180820380600f0b8118613bd8579050905080820180600f0b8118613bd8579050905061026052610240516101605160e05180820380600f0b8118613bd8579050905080820180600f0b8118613bd85790509050610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610260511361333a576000610260525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610240511361336b576000610240525b610220516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c016102405181556102605160018201556102805160028201556102a0516003820155506040511561352857426080511115613425576101e0516101005180820180600f0b8118613bd857905090506101e05260805160c05118613403576101e0516101805180820380600f0b8118613bd857905090506101e0525b6101e0516c050c783eb9b5c840000000000e6080516020526000526040600020555b4260c051111561347a5760805160c051111561347a57610200516101805180820380600f0b8118613bd8579050905061020052610200516c050c783eb9b5c840000000000e60c0516020526000526040600020555b6c050c783eb9b5c840000000000d60405160205260005260406000205460018101818110613bd85790506103a0526103a0516c050c783eb9b5c840000000000d604051602052600052604060002055426101a052436101c0526c050c783eb9b5c840000000000c60405160205260005260406000206103a051633b9ac9ff8111613bd85760021b810190506101605181556101805160018201556101a05160028201556101c0516003820155505b565b6c050c783eb9b5c840000000002054156135a45760156104a0527f616c6c20756e6c6f636b65642c6e6f2073656e736500000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b610440516104a052610460516104c0526009546104e0526104e05161040051808201828110613bd857905090506009556104a051610500526104c051610520526104a0516104005180607f1c613bd85780820180600f0b8118613bd857905090506104a052610420511561361b57610420516104c0525b600a6103e05160205260005260406000206104a05181556104c0516001820155506103e05160405261050051606052610520516080526104a05160a0526104c05160c052613667612e17565b61040051156136eb576002546323b872dd610540526103e051610560523061058052610400516105a0526020610540606461055c6000855af16136af573d600060003e3d6000fd5b3d6136c657803b15613bd85760016105c0526136df565b60203d10613bd857610540518060011c613bd8576105c0525b6105c090505115613bd8575b6104c0516103e0517f4566dfc29f6f11d13a418c26a02bef7c28bae749d4de47e4e6a7cddea6730d596104005161054052610480516105605242610580526060610540a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c6104e051610540526104e05161040051808201828110613bd85790509050610560526040610540a1565b600060805260605160a05260006080905b8060c05260a0516080511061379f57613820565b60805160a051808201828110613bd8579050905060018101818110613bd85790508060011c905060e05260405160e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0160038101905054111561380e5760e05160018103818111613bd857905060a052613815565b60e0516080525b60010181811861378b575b5050608051815250565b600060805260605160a05260006080905b8060c05260a0516080511061384f576138d0565b60805160a051808201828110613bd8579050905060018101818110613bd85790508060011c905060e05260405160e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c016002810190505411156138be5760e05160018103818111613bd857905060a0526138c5565b60e0516080525b60010181811861383b575b5050608051815250565b600060a05260805160c05260006080905b8060e05260c05160a051106138ff57613998565b60a05160c051808201828110613bd8579050905060018101818110613bd85790508060011c9050610100526060516c050c783eb9b5c840000000000c604051602052600052604060002061010051633b9ac9ff8111613bd85760021b81019050600381019050541115613985576101005160018103818111613bd857905060c05261398d565b6101005160a0525b6001018181186138eb575b505060a051815250565b600060a05260805160c05260006080905b8060e05260c05160a051106139c757613a60565b60a05160c051808201828110613bd8579050905060018101818110613bd85790508060011c9050610100526060516c050c783eb9b5c840000000000c604051602052600052604060002061010051633b9ac9ff8111613bd85760021b81019050600281019050541115613a4d576101005160018103818111613bd857905060c052613a55565b6101005160a0525b6001018181186139b3575b505060a051815250565b60405160e052606051610100526080516101205260a051610140526101205162093a808104905062093a8081028162093a80820418613bd857905061016052600060ff905b80610180526101605162093a808101818110613bd85790506101605260006101a05260c0516101605111613b04576c050c783eb9b5c840000000000e610160516020526000526040600020546101a052613b0c565b60c051610160525b60e051610100516101605161012051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd8579050905060e05260c0516101605118613b6757613b97565b610100516101a05180820180600f0b8118613bd85790509050610100526101605161012052600101818118613aaf575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60e05113613bc857600060e0525b60e05160008112613bd857815250565b600080fda165767970657283000307000b", + "deployedBytecode": "0x6003361161000c57612d19565b60003560e01c34613bd857639bf6abf3811861047c576101c43610613bd8576004358060a01c613bd8576040526024356004016040813511613bd8578035806060526020820181816080375050506044356004016020813511613bd85780358060c05260208201803560e0525050506064358060a01c613bd857610100526084358060a01c613bd8576101205260a4358060a01c613bd8576101405260e4358060a01c613bd85761016052610104358060a01c613bd85761018052610124358060a01c613bd8576101a052610144358060011c613bd8576101c052610164358060a01c613bd8576101e0526c050c783eb9b5c84000000000155415610171576009610200527f6f6e6c79206f6e636500000000000000000000000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b60016c050c783eb9b5c840000000001555610100516101f0576006610200527f21656d70747900000000000000000000000000000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b610100516c050c783eb9b5c840000000001155600a6c050c783eb9b5c840000000001755600a6c050c783eb9b5c840000000001855426c050c783eb9b5c840000000001955610100516c050c783eb9b5c840000000001a5560405160025543600f5542600e5560405163313ce567610220526020610220600461023c845afa61027e573d600060003e3d6000fd5b60203d10613bd8576102209050516102005260066102005110156102a35760006102ac565b60ff6102005111155b610316576009610220527f21646563696d616c7300000000000000000000000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b60605180600355600081601f0160051c60028111613bd857801561034e57905b8060051b608001518160040155600101818118610336575b50505060c0518060065560e051600755506102005160085562093a8060c435101561037a576000610385565b63095f6a0060c43511155b6103ef576008610220527f216d61786c6f636b0000000000000000000000000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b60c435600155610120516c050c783eb9b5c840000000001255610140516c050c783eb9b5c840000000001355610160516c050c783eb9b5c840000000001c55610180516c050c783eb9b5c840000000001b556101a0516c050c783eb9b5c840000000001d556101c0516c050c783eb9b5c840000000001e556101e0516c050c783eb9b5c840000000001f55005b63fc0c546a811861049b5760043610613bd85760025460405260206040f35b6306fdde0381186105205760043610613bd8576020806040528060400160035480825260208201600082601f0160051c60028111613bd85780156104f257905b80600401548160051b8401526001018181186104db575b505050508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6395d89b4181186105785760043610613bd8576020806040528060400160065480825260208201600754815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b63313ce56781186105975760043610613bd85760085460405260206040f35b636b441a40811861060d5760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c8400000000011543318613bd8576040516c050c783eb9b5c8400000000014557f2f56810a6bf40af059b96d3aea4db54081f378029a518390491093a7b67032e960405160605260206060a1005b636a1c05ae811861068f5760043610613bd8576c050c783eb9b5c8400000000011543318613bd8576c050c783eb9b5c84000000000145460405260405115613bd8576040516c050c783eb9b5c8400000000011557febee2d5739011062cb4f14113f3b36bf0ffe3da5c0568f64189d1012a118910560405160605260206060a1005b6357f901e281186106d95760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c8400000000011543318613bd8576040516c050c783eb9b5c840000000000f55005b638e5b490f81186107215760043610613bd8576c050c783eb9b5c8400000000011543318613bd8576c050c783eb9b5c840000000000f546c050c783eb9b5c840000000001055005b6355a3323581186108695760243610613bd8576004358060011c613bd8576040526c050c783eb9b5c8400000000013543318156107b55760066060527f2161646d696e000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c050c783eb9b5c840000000001654604051186108295760076060527f616c72656164790000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c050c783eb9b5c8400000000016557f66515f71c349ef0ad8c6981cedaa58746200512e6e12754c5ac5cc701d5cf41860405160605260206060a1005b63655317ae8118610a445760243610613bd8576c050c783eb9b5c8400000000013543318156108ef5760066040527f2161646d696e000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b603260043511156109575760026040527f216b00000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6c050c783eb9b5c840000000001954603c8101818110613bd857905042116109d65760056040527f6561726c7900000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6c050c783eb9b5c8400000000017546c050c783eb9b5c8400000000018556004356c050c783eb9b5c840000000001755426c050c783eb9b5c8400000000019557f9c04360e3c3de1eeca25bbd4a21cdc22c4c192f4b91f91d2a0c59dcd042f8ba860043560405260206040a1005b63af3097af8118610b7c5760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000001354331815610ad85760066060527f2161646d696e000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b604051610b3c5760056060527f217a65726f00000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c050c783eb9b5c840000000001a557ff51c492eafc918620c2d49b196e6a4e0cac71709d348224a8e7fc231ee973e5960405160605260206060a1005b6366e01ca98118610c405760043610613bd8576c050c783eb9b5c840000000001254331815610c025760066040527f2161646d696e000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b60016c050c783eb9b5c8400000000020557f7ca88488f569b55d1fb073429f75839e505182c1f6d26e5caed22e9828df430c600160405260206040a1005b637c74a1748118610cc25760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000d6040516020526000526040600020546060526c050c783eb9b5c840000000000c6040516020526000526040600020606051633b9ac9ff8111613bd85760021b810190506001810190505460805260206080f35b63da020a188118610d245760443610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000c6040516020526000526040600020602435633b9ac9ff8111613bd85760021b810190506002810190505460605260206060f35b63adc635898118610d655760243610613bd8576004358060a01c613bd857604052600a60405160205260005260406000206001810190505460605260206060f35b63c2c4c5c18118610d885760043610613bd85760a036604037610d86612e17565b005b633a46273e8118610f345760443610613bd8576004358060a01c613bd8576105e052600054600214613bd8576002600055600a6105e05160205260005260406000208054610600526001810154610620525060243515613bd8576001610600511215610e54576016610640527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b426106205111610ee9576024610640527f43616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468610660527f64726177000000000000000000000000000000000000000000000000000000006106805261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b6105e0516103e05260243561040052600061042052600a6105e051602052600052604060002080546104405260018101546104605250600061048052610f2d61352a565b6003600055005b6365fc3873811861116a5760443610613bd857600054600214613bd857600260005533604052610f62612d1f565b60243562093a808104905062093a8081028162093a80820418613bd85790506105e052600a3360205260005260406000208054610600526001810154610620525060043515613bd857610600511561101a576019610640527f5769746864726177206f6c6420746f6b656e73206669727374000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b426105e051116110af576026610640527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e2074686520610660527f66757475726500000000000000000000000000000000000000000000000000006106805261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b42600154808201828110613bd857905090506105e0511115611131576014610640527f566f74696e67206c6f636b20746f6f206c6f6e670000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b336103e052600435610400526105e051610420526106005161044052610620516104605260016104805261116361352a565b6003600055005b634957677c81186112fc5760243610613bd857600054600214613bd857600260005533604052611198612d1f565b600a33602052600052604060002080546105e0526001810154610600525060043515613bd85760016105e0511215611230576016610620527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006106405261062050610620518061064001601f826000031636823750506308c379a06105e052602061060052601f19601f6106205101166044016105fcfd5b4261060051116112c5576024610620527f43616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468610640527f64726177000000000000000000000000000000000000000000000000000000006106605261062050610620518061064001601f826000031636823750506308c379a06105e052602061060052601f19601f6106205101166044016105fcfd5b336103e052600435610400526000610420526105e0516104405261060051610460526002610480526112f561352a565b6003600055005b63eff7a612811861157a5760243610613bd857600054600214613bd85760026000553360405261132a612d1f565b600a33602052600052604060002080546105e0526001810154610600525060043562093a808104905062093a8081028162093a80820418613bd8579050610620524261060051116113db57600c610640527f4c6f636b206578706972656400000000000000000000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b60016105e051121561144d576011610640527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b6106005161062051116114c057601f610640527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b42600154808201828110613bd85790509050610620511115611542576014610640527f566f74696e67206c6f636b20746f6f206c6f6e670000000000000000000000006106605261064050610640518061066001601f826000031636823750506308c379a061060052602061062052601f19601f61064051011660440161061cfd5b336103e05260006104005261062051610420526105e05161044052610600516104605260036104805261157361352a565b6003600055005b633ccfd60b81186117cf5760043610613bd857600054600214613bd8576002600055600a33602052600052604060002080546103e05260018101546104005250610400514210156115d9576c050c783eb9b5c8400000000020546115dc565b60015b611646576017610420527f6c6f636b2021657870697265206f722021756e6c6f636b0000000000000000006104405261042050610420518061044001601f826000031636823750506308c379a06103e052602061040052601f19601f6104205101166044016103fcfd5b6103e05160008112613bd857610420526103e05161044052610400516104605260006104005260006103e052600a3360205260005260406000206103e051815561040051600182015550600954610480526104805161042051808203828111613bd857905090506009553360405261044051606052610460516080526103e05160a0526104005160c0526116d8612e17565b60025463a9059cbb6104a052336104c052610420516104e05260206104a060446104bc6000855af161170f573d600060003e3d6000fd5b3d61172657803b15613bd85760016105005261173f565b60203d10613bd8576104a0518060011c613bd857610500525b61050090505115613bd857337ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610420516104a052426104c05260406104a0a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c610480516104a0526104805161042051808203828111613bd857905090506104c05260406104a0a16003600055005b638239f0648118611c6e5760043610613bd857600054600214613bd857600260005560016c050c783eb9b5c840000000001654181561186e57600d6103e0527f216561726c7920756e6c6f636b00000000000000000000000000000000000000610400526103e0506103e0518061040001601f826000031636823750506308c379a06103a05260206103c052601f19601f6103e05101166044016103bcfd5b600a33602052600052604060002080546103e052600181015461040052506104005142106118fc57600c610420527f6c6f636b206578706972656400000000000000000000000000000000000000006104405261042050610420518061044001601f826000031636823750506308c379a06103e052602061040052601f19601f6104205101166044016103fcfd5b6103e05160008112613bd857610420526104005142808203828111613bd85790509050610440526000610460526c050c783eb9b5c840000000001954603c8101818110613bd85790504211611963576c050c783eb9b5c84000000000185461046052611977565b6c050c783eb9b5c840000000001754610460525b61044051670de0b6b3a7640000810281670de0b6b3a7640000820418613bd85790506001548015613bd8578082049050905061046051808202811583838304141715613bd85790509050610480526104205161048051808202811583838304141715613bd85790509050670de0b6b3a764000081049050600a810490506104a052610420516104a0511115611a0f57610420516104a0525b610420516104a051808203828111613bd857905090506104c0526103e0516104e052610400516105005260006104005260006103e052600a3360205260005260406000206103e051815561040051600182015550600954610520526105205161042051808203828111613bd85790509050600955336040526104e051606052610500516080526103e05160a0526104005160c052611aab612e17565b6104a05115611b355760025463a9059cbb610540526c050c783eb9b5c840000000001a54610560526104a051610580526020610540604461055c6000855af1611af9573d600060003e3d6000fd5b3d611b1057803b15613bd85760016105a052611b29565b60203d10613bd857610540518060011c613bd8576105a0525b6105a090505115613bd8575b6104c05115611bb15760025463a9059cbb6105405233610560526104c051610580526020610540604461055c6000855af1611b75573d600060003e3d6000fd5b3d611b8c57803b15613bd85760016105a052611ba5565b60203d10613bd857610540518060011c613bd8576105a0525b6105a090505115613bd8575b337ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610420516105405242610560526040610540a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c61052051610540526105205161042051808203828111613bd85790509050610560526040610540a1337ff11a1a5a36ad286d4f77c166e8d87e5bfe87d1e74f9c46654ef21bd811c6784f6104a0516105405261044051610560526040610540a26003600055005b6370a082318118611c8b5760243610613bd8574261014052611ca5565b62fdd58e8118611e345760443610613bd857602435610140525b6004358060a01c613bd85761012052600061016052426101405118611ceb576c050c783eb9b5c840000000000d6101205160205260005260406000205461016052611d2e565b61012051604052610140516060526c050c783eb9b5c840000000000d61012051602052600052604060002054608052611d256101806139a2565b61018051610160525b61016051611d4a576000610180526020610180611e3256611e32565b6c050c783eb9b5c840000000000c61012051602052600052604060002061016051633b9ac9ff8111613bd85760021b8101905080546101805260018101546101a05260028101546101c05260038101546101e05250610180516101a051610140516101c051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd85790509050610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113611e1c576000610180525b6101805160008112613bd8576102005260206102005bf35b634ee2cd7e811861210f5760443610613bd8576004358060a01c613bd857610120524360243511613bd857610120516040526024356060526c050c783eb9b5c840000000000d61012051602052600052604060002054608052611e986101606138da565b61016051610140526c050c783eb9b5c840000000000c61012051602052600052604060002061014051633b9ac9ff8111613bd85760021b8101905080546101605260018101546101805260028101546101a05260038101546101c05250600b546101e0526024356040526101e051606052611f1461022061377a565b6102205161020052610200516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c01805461022052600181015461024052600281015461026052600381015461028052506040366102a0376101e0516102005110611fa3574361028051808203828111613bd857905090506102a0524261026051808203828111613bd857905090506102c052612025565b6102005160018101818110613bd85790506c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0180546102e052600181015461030052600281015461032052600381015461034052506103405161028051808203828111613bd857905090506102a0526103205161026051808203828111613bd857905090506102c0525b610260516102e0526102a0511561208b576102e0516102c05160243561028051808203828111613bd85790509050808202811583838304141715613bd857905090506102a0518015613bd85780820490509050808201828110613bd857905090506102e0525b61016051610180516102e0516101a051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd857905090506101605260006101605112156120f757600061030052602061030061210d5661210d565b6101605160008112613bd8576103005260206103005bf35b6318160ddd811861212c5760043610613bd857426101c052612147565b63bd85b03981186122165760243610613bd8576004356101c0525b60006101e052426101c0511861216357600b546101e052612184565b6101c051604052600b5460605261217b61020061382a565b610200516101e0525b6101e0516121a057600061020052602061020061221456612214565b6101e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c018054610200526001810154610220526002810154610240526003810154610260525060206102005160405261022051606052610240516080526102605160a0526101c05160c052612210610280613a6a565b6102805bf35b63981b24d081186124235760243610613bd8574360043511613bd857600b546101c0526004356040526101c05160605261225161020061377a565b610200516101e0526101e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c01805461020052600181015461022052600281015461024052600381015461026052506000610280526101c0516101e0511061231b574361026051146123dc5760043561026051808203828111613bd857905090504261024051808203828111613bd85790509050808202811583838304141715613bd857905090504361026051808203828111613bd857905090508015613bd85780820490509050610280526123dc565b6101e05160018101818110613bd85790506c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0180546102a05260018101546102c05260028101546102e052600381015461030052506103005161026051146123dc5760043561026051808203828111613bd857905090506102e05161024051808203828111613bd85790509050808202811583838304141715613bd857905090506103005161026051808203828111613bd857905090508015613bd85780820490509050610280525b60206102005160405261022051606052610240516080526102605160a0526102405161028051808201828110613bd8579050905060c05261241e6102a0613a6a565b6102a0f35b63db93cc7a811861265e5760043610613bd857600054600214613bd85760026000556c050c783eb9b5c840000000001b54636a627842604052600254606052602060406024605c6000855af161247e573d600060003e3d6000fd5b60203d10613bd857604050506c050c783eb9b5c840000000001c546370a0823160605230608052602060606024607c845afa6124bf573d600060003e3d6000fd5b60203d10613bd857606090505160405260405115612657576c050c783eb9b5c840000000001f546c050c783eb9b5c840000000001d54186125d4576c050c783eb9b5c840000000001c5463095ea7b36060526c050c783eb9b5c840000000001f5460805260405160a052602060606044607c6000855af1612545573d600060003e3d6000fd5b3d61255b57803b15613bd857600160c052612572565b60203d10613bd8576060518060011c613bd85760c0525b60c090505115613bd8576c050c783eb9b5c840000000001f5463338b5dea6060526c050c783eb9b5c840000000001c5460805260405160a052803b15613bd857600060606044607c6000855af16125ce573d600060003e3d6000fd5b50612657565b6c050c783eb9b5c840000000001c5463a9059cbb6060526c050c783eb9b5c840000000001d5460805260405160a052602060606044607c6000855af161261f573d600060003e3d6000fd5b3d61263557803b15613bd857600160c05261264c565b60203d10613bd8576060518060011c613bd85760c0525b60c090505115613bd8575b6003600055005b63cb8e090d81186128065760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c8400000000011543318156126f25760066060527f2161646d696e000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c050c783eb9b5c840000000001e5461276257600a6060527f21617661696c61626c650000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516127c65760066060527f21656d707479000000000000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c050c783eb9b5c840000000001d557f454ccf21cdac2e344d64173597f5922657c25c5b1e6c3f733791637513d2063760405160605260206060a1005b63ee00ef3a81186128255760043610613bd85760015460405260206040f35b6382bfefc881186128445760043610613bd85760025460405260206040f35b63047fc9aa81186128635760043610613bd85760095460405260206040f35b63cbf9fe5f81186128a85760243610613bd8576004358060a01c613bd857604052600a6040516020526000526040600020805460605260018101546080525060406060f35b63900cf0cf81186128c75760043610613bd857600b5460405260206040f35b63d1febfb9811861291b5760243610613bd8576004356c01431e0fae6d7217ca9fffffff8111613bd85760021b600c01805460405260018101546060526002810154608052600381015460a0525060806040f35b6328d09d4781186129915760443610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000c6040516020526000526040600020602435633b9ac9ff8111613bd85760021b8101905080546060526001810154608052600281015460a052600381015460c0525060806060f35b63010ae75781186129d85760243610613bd8576004358060a01c613bd8576040526c050c783eb9b5c840000000000d60405160205260005260406000205460605260206060f35b63711974848118612a115760243610613bd8576c050c783eb9b5c840000000000e60043560205260005260406000205460405260206040f35b638ff36fd18118612a3c5760043610613bd8576c050c783eb9b5c840000000000f5460405260206040f35b637175d4f78118612a675760043610613bd8576c050c783eb9b5c84000000000105460405260206040f35b63f851a4408118612a925760043610613bd8576c050c783eb9b5c84000000000115460405260206040f35b63142614258118612abd5760043610613bd8576c050c783eb9b5c84000000000125460405260206040f35b6322cf35f58118612ae85760043610613bd8576c050c783eb9b5c84000000000135460405260206040f35b6317f7182a8118612b135760043610613bd8576c050c783eb9b5c84000000000145460405260206040f35b639a01873c8118612b3e5760043610613bd8576c050c783eb9b5c84000000000155460405260206040f35b63f68467278118612b695760043610613bd8576c050c783eb9b5c84000000000165460405260206040f35b63cd8c79aa8118612b945760043610613bd8576c050c783eb9b5c84000000000175460405260206040f35b63094cda238118612bbf5760043610613bd8576c050c783eb9b5c84000000000185460405260206040f35b63205ad4088118612bea5760043610613bd8576c050c783eb9b5c84000000000195460405260206040f35b635836ec3a8118612c155760043610613bd8576c050c783eb9b5c840000000001a5460405260206040f35b6373f43d6d8118612c405760043610613bd8576c050c783eb9b5c840000000001b5460405260206040f35b6338d546458118612c6b5760043610613bd8576c050c783eb9b5c840000000001c5460405260206040f35b631dac30b08118612c965760043610613bd8576c050c783eb9b5c840000000001d5460405260206040f35b63b027651a8118612cc15760043610613bd8576c050c783eb9b5c840000000001e5460405260206040f35b63acc2166a8118612cec5760043610613bd8576c050c783eb9b5c840000000001f5460405260206040f35b63b3d8f7e78118612d175760043610613bd8576c050c783eb9b5c84000000000205460405260206040f35b505b60006000fd5b3260405114612e15576c050c783eb9b5c84000000000105460605260605115612d945760605163c23697a860805260405160a052602060806024609c6000855af1612d6f573d600060003e3d6000fd5b60203d10613bd8576080518060011c613bd85760c05260c090505115612d9457612e15565b60256080527f536d61727420636f6e7472616374206465706f7369746f7273206e6f7420616c60a0527f6c6f77656400000000000000000000000000000000000000000000000000000060c0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b565b6101403660e037600b546102205260405115612f83574260805111612e3d576000612e45565b600160605112155b15612ea65760605160015480607f1c613bd8578015613bd85780820580600f0b8118613bd85790509050610100526101005160805142808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905060e0525b4260c05111612eb6576000612ebe565b600160a05112155b15612f205760a05160015480607f1c613bd8578015613bd85780820580600f0b8118613bd85790509050610180526101805160c05142808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd85790509050610160525b6c050c783eb9b5c840000000000e6080516020526000526040600020546101e05260c05115612f835760805160c05118612f61576101e05161020052612f83565b6c050c783eb9b5c840000000000e60c051602052600052604060002054610200525b604036610240374261028052436102a0526102205115612fde57610220516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0180546102405260018101546102605260028101546102805260038101546102a052505b610280516102c052610240516102e052610260516103005261028051610320526102a051610340526000610360526102805142111561306d57436102a051808203828111613bd85790509050670de0b6b3a7640000810281670de0b6b3a7640000820418613bd85790504261028051808203828111613bd857905090508015613bd85780820490509050610360525b6102c05162093a808104905062093a8081028162093a80820418613bd857905061038052600060ff905b806103a0526103805162093a808101818110613bd85790506103805260006103c0524261038051116130ea576c050c783eb9b5c840000000000e610380516020526000526040600020546103c0526130f0565b42610380525b6102405161026051610380516102c051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd8579050905061024052610260516103c05180820180600f0b8118613bd85790509050610260527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610240511361318a576000610240525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61026051136131bb576000610260525b610380516102c052610380516102805261034051610360516103805161032051808203828111613bd85790509050808202811583838304141715613bd85790509050670de0b6b3a764000081049050808201828110613bd857905090506102a0526102205160018101818110613bd85790506102205242610380511861324957436102a0526132955661328a565b610220516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c016102405181556102605160018201556102805160028201556102a0516003820155505b600101818118613097575b505061022051600b556040511561336b5761026051610180516101005180820380600f0b8118613bd8579050905080820180600f0b8118613bd8579050905061026052610240516101605160e05180820380600f0b8118613bd8579050905080820180600f0b8118613bd85790509050610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610260511361333a576000610260525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610240511361336b576000610240525b610220516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c016102405181556102605160018201556102805160028201556102a0516003820155506040511561352857426080511115613425576101e0516101005180820180600f0b8118613bd857905090506101e05260805160c05118613403576101e0516101805180820380600f0b8118613bd857905090506101e0525b6101e0516c050c783eb9b5c840000000000e6080516020526000526040600020555b4260c051111561347a5760805160c051111561347a57610200516101805180820380600f0b8118613bd8579050905061020052610200516c050c783eb9b5c840000000000e60c0516020526000526040600020555b6c050c783eb9b5c840000000000d60405160205260005260406000205460018101818110613bd85790506103a0526103a0516c050c783eb9b5c840000000000d604051602052600052604060002055426101a052436101c0526c050c783eb9b5c840000000000c60405160205260005260406000206103a051633b9ac9ff8111613bd85760021b810190506101605181556101805160018201556101a05160028201556101c0516003820155505b565b6c050c783eb9b5c840000000002054156135a45760156104a0527f616c6c20756e6c6f636b65642c6e6f2073656e736500000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b610440516104a052610460516104c0526009546104e0526104e05161040051808201828110613bd857905090506009556104a051610500526104c051610520526104a0516104005180607f1c613bd85780820180600f0b8118613bd857905090506104a052610420511561361b57610420516104c0525b600a6103e05160205260005260406000206104a05181556104c0516001820155506103e05160405261050051606052610520516080526104a05160a0526104c05160c052613667612e17565b61040051156136eb576002546323b872dd610540526103e051610560523061058052610400516105a0526020610540606461055c6000855af16136af573d600060003e3d6000fd5b3d6136c657803b15613bd85760016105c0526136df565b60203d10613bd857610540518060011c613bd8576105c0525b6105c090505115613bd8575b6104c0516103e0517f4566dfc29f6f11d13a418c26a02bef7c28bae749d4de47e4e6a7cddea6730d596104005161054052610480516105605242610580526060610540a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c6104e051610540526104e05161040051808201828110613bd85790509050610560526040610540a1565b600060805260605160a05260006080905b8060c05260a0516080511061379f57613820565b60805160a051808201828110613bd8579050905060018101818110613bd85790508060011c905060e05260405160e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c0160038101905054111561380e5760e05160018103818111613bd857905060a052613815565b60e0516080525b60010181811861378b575b5050608051815250565b600060805260605160a05260006080905b8060c05260a0516080511061384f576138d0565b60805160a051808201828110613bd8579050905060018101818110613bd85790508060011c905060e05260405160e0516c01431e0fae6d7217ca9fffffff8111613bd85760021b600c016002810190505411156138be5760e05160018103818111613bd857905060a0526138c5565b60e0516080525b60010181811861383b575b5050608051815250565b600060a05260805160c05260006080905b8060e05260c05160a051106138ff57613998565b60a05160c051808201828110613bd8579050905060018101818110613bd85790508060011c9050610100526060516c050c783eb9b5c840000000000c604051602052600052604060002061010051633b9ac9ff8111613bd85760021b81019050600381019050541115613985576101005160018103818111613bd857905060c05261398d565b6101005160a0525b6001018181186138eb575b505060a051815250565b600060a05260805160c05260006080905b8060e05260c05160a051106139c757613a60565b60a05160c051808201828110613bd8579050905060018101818110613bd85790508060011c9050610100526060516c050c783eb9b5c840000000000c604051602052600052604060002061010051633b9ac9ff8111613bd85760021b81019050600281019050541115613a4d576101005160018103818111613bd857905060c052613a55565b6101005160a0525b6001018181186139b3575b505060a051815250565b60405160e052606051610100526080516101205260a051610140526101205162093a808104905062093a8081028162093a80820418613bd857905061016052600060ff905b80610180526101605162093a808101818110613bd85790506101605260006101a05260c0516101605111613b04576c050c783eb9b5c840000000000e610160516020526000526040600020546101a052613b0c565b60c051610160525b60e051610100516101605161012051808203828111613bd8579050905080607f1c613bd85780820280600f0b8118613bd8579050905080820380600f0b8118613bd8579050905060e05260c0516101605118613b6757613b97565b610100516101a05180820180600f0b8118613bd85790509050610100526101605161012052600101818118613aaf575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60e05113613bc857600060e0525b60e05160008112613bd857815250565b600080fda165767970657283000307000b", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/apps/api/src/app/hardhat/hardhat.config.ts b/apps/api/src/app/hardhat/hardhat.config.ts new file mode 100644 index 000000000..c30262176 --- /dev/null +++ b/apps/api/src/app/hardhat/hardhat.config.ts @@ -0,0 +1,114 @@ +import dotenv from 'dotenv'; +import { HardhatUserConfig } from 'hardhat/config'; +import '@nomicfoundation/hardhat-toolbox'; + +dotenv.config(); + +const INFURA_PROJECT_ID = process.env.INFURA_PROJECT_ID || ''; +const POLYGON_PRIVATE_KEY = process.env.POLYGON_PRIVATE_KEY || ''; + +const config: HardhatUserConfig = { + defaultNetwork: 'hardhat', + networks: { + hardhat: { + accounts: [ + { + balance: '100000000000000000000', + privateKey: '0x873c254263b17925b686f971d7724267710895f1585bb0533db8e693a2af32ff', + }, + { + balance: '100000000000000000000', + privateKey: '0x97093724e1748ebfa6aa2d2ec4ec68df8678423ab9a12eb2d27ddc74e35e5db9', + }, + { + balance: '100000000000000000000', + privateKey: '5a05e38394194379795422d2e8c1d33e90033d90defec4880174c39198f707e3', + }, + { + balance: '100000000000000000000', + privateKey: 'eea0247bd059ac4d2528adb36bb0de003d62ba568e3197984b61c41d9a132df0', + }, + ], + }, + }, + paths: { + sources: 'contracts', + }, + solidity: { + compilers: [ + { + version: '0.5.17', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.7.6', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.8.0', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.8.1', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.8.2', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.8.6', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.8.24', // Remove when Lock is removed + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + ], + }, +}; + +if (POLYGON_PRIVATE_KEY && INFURA_PROJECT_ID && config.networks) { + config.networks.matic = { + url: `https://polygon-mainnet.infura.io/v3/${INFURA_PROJECT_ID}`, + accounts: [POLYGON_PRIVATE_KEY], + timeout: 2483647, + }; +} + +export default config; diff --git a/apps/api/src/app/hardhat/index.ts b/apps/api/src/app/hardhat/index.ts new file mode 100644 index 000000000..577f7074e --- /dev/null +++ b/apps/api/src/app/hardhat/index.ts @@ -0,0 +1,172 @@ +import { ContractNetworksConfig } from '@safe-global/protocol-kit'; +// Safe +import DefaultCallbackHandler from './export/DefaultCallbackHandler.json'; +import CreateCall from './export/CreateCall.json'; +import GnosisSafeL2 from './export/GnosisSafeL2.json'; +import GnosisSafeProxyFactory from './export/GnosisSafeProxyFactory.json'; +import MultiSend from './export/MultiSend.json'; +import MultiSendCallOnly from './export/MultiSendCallOnly.json'; +import SignMessageLib from './export/SignMessageLib.json'; +import SimulateTxAccessor from './export/SimulateTxAccessor.json'; +// Balancer +import USDC from './export/USDC.json'; +import THX from './export/THX.json'; +import BPT from './export/BPT.json'; +import BPTGauge from './export/BPTGauge.json'; +import BalancerVault from './export/BalancerVault.json'; +import BalancerMinter from './export/BalancerMinter.json'; +import BalancerGaugeController from './export/BalancerGaugeController.json'; +import BAL from './export/BAL.json'; +import Launchpad from './export/Launchpad.json'; +// Tokens +import THXERC20_LimitedSupply from './export/THXERC20_LimitedSupply.json'; +import THXERC20_UnlimitedSupply from './export/THXERC20_UnlimitedSupply.json'; +import THXERC721 from './export/THXERC721.json'; +import THXERC1155 from './export/THXERC1155.json'; +// Protocol +import THXPaymentSplitter from './export/THXPaymentSplitter.json'; +import THXRegistry from './export/THXRegistry.json'; +import VotingEscrow from './export/VotingEscrow.json'; +import RewardDistributor from './export/RewardDistributor.json'; +import RewardFaucet from './export/RewardFaucet.json'; +import SmartWalletWhitelist from './export/SmartWalletWhitelist.json'; +import LensReward from './export/LensReward.json'; + +export const getArtifact = (contractName: TContractName) => { + if (!contractArtifacts[contractName]) { + throw new Error(`Contract ${contractName} not found in contractArtifacts`); + } + return contractArtifacts[contractName]; +}; + +export const contractNetworks = { + '31337': { + // Safe + simulateTxAccessorAddress: '0x278Ff6d33826D906070eE938CDc9788003749e93', + safeProxyFactoryAddress: '0xEAB9a65eB0F098f822033192802B53EE159De5F0', + fallbackHandlerAddress: '0x055cBfeD6df4AFE2452b18fd3D2592D1795592b4', + createCallAddress: '0xb63564A81D5d4004F4f22E9aB074cE25540B0C26', + multiSendAddress: '0x50aF0922d65D04D87d810048Dc640E2474eBfbd9', + multiSendCallOnlyAddress: '0x15FC0878406CcF4d2963235A5B1EF68C67F17Ee5', + signMessageLibAddress: '0xa4E84979c95cD4f12C53E73d63E0A8634A1f44Ae', + safeMasterCopyAddress: '0xd916a690676e925Ac9Faf2d01869c13Fd9757ef2', + + // Tokens + THX: '0xB952d9b5de7804691e7936E88915A669B15822ef', + USDC: '0x7150A3CC09429583471020A6CE5228A57736180a', + BAL: '0xe1c01805a21ee0DC535afa93172a5F21CE160649', + BPT: '0xf228ADAa4c3D07C8285C1025421afe2c4F320C59', + BPTGauge: '0x8613B8E442219e4349fa5602C69431131a7ED114', + BalancerVault: '0x8B219D3d1FC64e03F6cF3491E7C7A732bF253EC8', + + // veTHX + VotingEscrow: '0x1280809d06C42E68063305235813e52c8Bb03a58', + RewardDistributor: '0xd0507c5363AeCfe8231FF4110e05AFf611d7F7B6', + RewardFaucet: '0x33599eaec2752DB3242323483A7313bA3b1111cd', + SmartWalletWhitelist: '0xb3B2b0fc5ce12aE58EEb13E19547Eb2Dd61A79D5', + LensReward: '0x774442713f32fa98bf27bEc78c96fb7186f7C223', + + // Company + THXRegistry: '0x0Bb5Cb54566cEEf9dF1F60d8D7d2Fd01eA88279e', + THXPaymentSplitter: '0x58C0e64cBB7E5C7D0201A3a5c2D899cC70B0dc4c', + + CompanyMultiSig: '0xaf9d56684466fcFcEA0a2B7fC137AB864d642946', + }, + '137': { + // Tokens + BPT: '0xb204BF10bc3a5435017D3db247f56dA601dFe08A', + BPTGauge: '0xf16BECC1Bcaf0fF0b865024a644a4da1A2f8585c', + BalancerVault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + BAL: '0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3', + USDC: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + THX: '0x2934b36ca9A4B31E633C5BE670C8C8b28b6aA015', + + // veTHX + VotingEscrow: '0xE3B8E734e7BCcB64B63e032795896CC57012A51D', + RewardDistributor: '0xCc62c812EfF9cA4c35623103B2Bb63E22f465E09', + RewardFaucet: '0xA1D7671f73FbcB5e079d4dC4Cffb7dDD0967EA7E', + SmartWalletWhitelist: '0x876625a92cEAa7f1Bddd40908B8eb5C6080cB83C', + LensReward: '0xE8D9624E0B7f839540E7c13577550E3Eff3FC8aA', + + // Company + THXRegistry: '', + THXPaymentSplitter: '', + CompanyMultiSig: '0x0b8e0aAF940cc99EDA5DA5Ab0a8d6Ed798eDc08A', + }, + '1': { + BalancerGaugeController: '0xC128468b7Ce63eA702C1f104D55A2566b13D3ABD', + BalancerRootGauge: '0x9902913ce5439d667774c8f9526064b2bc103b4a', + }, +} as ContractNetworksConfig & any; + +export type TContractName = (typeof contractNames)[number]; + +export const contractArtifacts: { [contractName: string]: { abi: any; bytecode: string } } = { + // Safe + DefaultCallbackHandler, + CreateCall, + GnosisSafeL2, + GnosisSafeProxyFactory, + MultiSend, + MultiSendCallOnly, + SignMessageLib, + SimulateTxAccessor, + // Balancer + USDC, + THX, + BPT, + BPTGauge, + BalancerVault, + BalancerMinter, + BalancerGaugeController, + BAL, + Launchpad, + // Tokens + THXERC20_LimitedSupply, + THXERC20_UnlimitedSupply, + THXERC721, + THXERC1155, + // Protocol + THXPaymentSplitter, + THXRegistry, + VotingEscrow, + RewardDistributor, + RewardFaucet, + SmartWalletWhitelist, + LensReward, +}; + +export const contractNames = [ + // Safe + 'DefaultCallbackHandler', + 'CreateCall', + 'GnosisSafeL2', + 'GnosisSafeProxyFactory', + 'MultiSend', + 'MultiSendCallOnly', + 'SignMessageLib', + 'SimulateTxAccessor', + // Balancer + 'USDC', + 'THX', + 'BPT', + 'BPTGauge', + 'BalancerVault', + 'BalancerMinter', + 'BalancerGaugeController', + 'BAL', + 'Launchpad', + // Tokens + 'THXERC20_LimitedSupply', + 'THXERC20_UnlimitedSupply', + 'THXERC721', + 'THXERC1155', + // Protocol + 'THXPaymentSplitter', + 'THXRegistry', + 'VotingEscrow', + 'RewardDistributor', + 'RewardFaucet', + 'SmartWalletWhitelist', + 'LensReward', +] as const; diff --git a/apps/api/src/app/hardhat/scripts/deploy.ts b/apps/api/src/app/hardhat/scripts/deploy.ts new file mode 100644 index 000000000..a84ddbdd1 --- /dev/null +++ b/apps/api/src/app/hardhat/scripts/deploy.ts @@ -0,0 +1,116 @@ +import hre from 'hardhat'; +import { parseUnits } from 'ethers/lib/utils'; +import { contractArtifacts, getArtifact, TContractName } from '..'; +import { Signer } from '@ethersproject/abstract-signer'; + +const deploy = async (contractName: TContractName, args: string[], signer: Signer) => { + const artifact = getArtifact(contractName); + const factory = new hre.ethers.ContractFactory(artifact.abi, artifact.bytecode, signer); + const contract = await factory.deploy(...args); + console.log(`${contractName} ${contract.address}`); + return contract; +}; + +async function main() { + const [signer] = (await hre.ethers.getSigners()) as unknown as Signer[]; + // Deploy Safe infrastructure + for (const contractName of [ + 'SimulateTxAccessor', + 'GnosisSafeProxyFactory', + 'DefaultCallbackHandler', + 'CreateCall', + 'MultiSend', + 'MultiSendCallOnly', + 'SignMessageLib', + 'GnosisSafeL2', + ] as TContractName[]) { + await deploy(contractName, [], signer); + } + // Deploy Balancer infrastructure + const totalSupply = parseUnits('1000000', 'ether').toString(); + const thx = await deploy('THX', [await signer.getAddress(), totalSupply], signer); + const usdc = await deploy('USDC', [await signer.getAddress(), totalSupply], signer); + const bal = await deploy('BAL', [await signer.getAddress(), totalSupply], signer); + const bpt = await deploy('BPT', [await signer.getAddress(), totalSupply], signer); + const gauge = await deploy('BPTGauge', [bpt.address], signer); + const vault = await deploy('BalancerVault', [bpt.address, usdc.address, thx.address], signer); + + await bpt.setVault(vault.address); + await bpt.transfer(vault.address, parseUnits('500000', 'ether').toString()); + + // Deploy Balancer Launchpad Implementations + const VotingEscrow = await deploy('VotingEscrow', [], signer); + const RewardDistributor = await deploy('RewardDistributor', [], signer); + const RewardFaucet = await deploy('RewardFaucet', [], signer); + + // Deploy Balancer Launchpad + const BalMinter = await deploy('BalancerMinter', [bal.address], signer); + const Launchpad = await deploy( + 'Launchpad', + [VotingEscrow.address, RewardDistributor.address, RewardFaucet.address, bal.address, BalMinter.address], + signer, + ); + + /* + @notice Deploys new VotingEscrow, RewardDistributor and RewardFaucet contracts + @param tokenBptAddr The address of the token to be used for locking + @param name The name for the new VotingEscrow contract + @param symbol The symbol for the new VotingEscrow contract + @param maxLockTime A constraint for the maximum lock time in the new VotingEscrow contract + @param rewardDistributorStartTime The start time for reward distribution + @param admin_unlock_all Admin address to enable unlock-all feature in VotingEscrow (zero-address to disable forever) + @param admin_early_unlock Admin address to enable eraly-unlock feature in VotingEscrow (zero-address to disable forever) + @param rewardReceiver The receiver address of claimed BAL-token rewards + */ + const lp = new hre.ethers.Contract(Launchpad.address, contractArtifacts['Launchpad'].abi, signer); + let tx = await lp.deploy( + gauge.address, + 'Voted Escrow 20USDC-80THX-gauge', + 'veTHX', + 7776000, // 90 days + Math.ceil(Date.now() / 1000) + 60 * 60 * 24 * 7, // 7 days from now + await signer.getAddress(), // admin_unlock_all + await signer.getAddress(), // admin_early_unlock + '0x0000000000000000000000000000000000000000', // empty will set it to the rewardDistributor + ); + tx = await tx.wait(); + + const event = tx.events.find((event: any) => event.event == 'VESystemCreated'); + const { votingEscrow, rewardDistributor, rewardFaucet } = event.args; + console.log(`VotingEscrow ${votingEscrow}`); + console.log(`RewardDistributor ${rewardDistributor}`); + console.log(`RewardFaucet ${rewardFaucet}`); + + // Configure VeTHX + const vethx = new hre.ethers.Contract(votingEscrow, contractArtifacts['VotingEscrow'].abi, signer); + const rdthx = new hre.ethers.Contract(rewardDistributor, contractArtifacts['RewardDistributor'].abi, signer); + const smartCheckerList = await deploy('SmartWalletWhitelist', [await signer.getAddress()], signer); + const lensReward = await deploy('LensReward', [], signer); + + // Configure reward tokens in reward distributor + await rdthx.addAllowedRewardTokens([bal.address, bpt.address]); + + // Add smart wallet whitelist checker + await vethx.commit_smart_wallet_checker(smartCheckerList.address); + await vethx.apply_smart_wallet_checker(); + await vethx.set_early_unlock(true); + // await vethx.set_early_unlock_penalty_speed(1); //Default + // Set early exit penalty treasury to reward distributor + await vethx.set_penalty_treasury(rewardDistributor); + // Allow all contract wallets + await smartCheckerList.setAllowAll(true); + + // Deploy THXRegistry + const registry = await deploy( + 'THXRegistry', + [usdc.address, await signer.getAddress(), rdthx.address, gauge.address], + signer, + ); + await registry.setPayoutRate('3000'); // 30% + + // Deploy PaymentSplitter + const splitter = await deploy('THXPaymentSplitter', [await signer.getAddress(), registry.address], signer); + await splitter.setRegistry(registry.address); +} + +main().catch(console.error); diff --git a/apps/api/src/app/hardhat/tsconfig.json b/apps/api/src/app/hardhat/tsconfig.json new file mode 100644 index 000000000..fe59b369b --- /dev/null +++ b/apps/api/src/app/hardhat/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true + }, + "include": ["./**/*.ts", "./**/*.json"] +} diff --git a/apps/api/src/app/index.ts b/apps/api/src/app/index.ts new file mode 100644 index 000000000..fac02b9bb --- /dev/null +++ b/apps/api/src/app/index.ts @@ -0,0 +1,53 @@ +import 'express-async-errors'; +import axios from 'axios'; +import axiosBetterStacktrace from 'axios-better-stacktrace'; +import compression from 'compression'; +import express, { Express, Request } from 'express'; +import lusca from 'lusca'; +import db from '@thxnetwork/api/util/database'; +import morganBody from 'morgan-body'; +import { router } from '@thxnetwork/api/controllers/index'; +import { MONGODB_URI, NODE_ENV, PORT, VERSION } from '@thxnetwork/api/config/secrets'; +import { corsHandler, errorLogger, errorNormalizer, errorOutput, notFoundHandler } from '@thxnetwork/api/middlewares'; +import { assetsPath } from './util/path'; +import morgan from './middlewares/morgan'; + +axiosBetterStacktrace(axios); + +const app: Express = express(); + +db.connect(MONGODB_URI); + +app.set('trust proxy', true); +app.set('port', PORT); +app.use(lusca.xframe('SAMEORIGIN')); +app.use(lusca.xssProtection(true)); +app.use(express.static(assetsPath)); +app.use( + express.json({ + verify(req: Request, res, buf, encoding: BufferEncoding) { + if (buf && buf.length) { + req.rawBody = buf.toString(encoding || 'utf8'); + } + }, + }), +); + +app.use(morgan); + +morganBody(app, { + logRequestBody: NODE_ENV === 'development', + logResponseBody: false, + skip: () => ['test', 'production'].includes(NODE_ENV), +}); + +app.use(express.urlencoded({ extended: true })); +app.use(corsHandler); +app.use(`/${VERSION}`, router); +app.use(notFoundHandler); +app.use(errorLogger); +app.use(errorNormalizer); +app.use(errorOutput); +app.use(compression()); + +export default app; diff --git a/apps/api/src/app/jobs/createTwitterQuests.ts b/apps/api/src/app/jobs/createTwitterQuests.ts new file mode 100644 index 000000000..27e02c0b3 --- /dev/null +++ b/apps/api/src/app/jobs/createTwitterQuests.ts @@ -0,0 +1,99 @@ +import { AccessTokenKind, QuestVariant, QuestSocialRequirement, OAuthRequiredScopes } from '@thxnetwork/common/enums'; +import { Pool, QuestSocial } from '@thxnetwork/api/models'; +import { logger } from '../util/logger'; +import { DASHBOARD_URL } from '../config/secrets'; +import TwitterDataProxy from '../proxies/TwitterDataProxy'; +import AccountProxy from '../proxies/AccountProxy'; +import MailService from '../services/MailService'; +import QuestService from '../services/QuestService'; + +export async function createTwitterQuests() { + for await (const pool of Pool.find({ 'settings.isTwitterSyncEnabled': true })) { + try { + const { hashtag, title, description, amount, locks, isPublished } = + pool.settings.defaults.conditionalRewards; + + const account = await AccountProxy.findById(pool.sub); + if (!account) { + logger.error(`Account not found for ${pool.sub}.`); + continue; + } + + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Twitter, + OAuthRequiredScopes.TwitterAutoQuest, + ); + if (!token) { + logger.error(`Could not find Twitter accounts for ${pool.sub} in ${pool.settings.title}`); + continue; + } + + const tweets = await TwitterDataProxy.searchTweets(account, `#${hashtag}`); + if (!tweets || !tweets.length) continue; + logger.info(`Found tweets matching the hashtag in the last 7 days!`); + logger.info(JSON.stringify(tweets)); + + const promises = tweets.map(async (tweet: any) => { + const isExistingQuest = !!(await QuestSocial.exists({ + poolId: String(pool._id), + content: tweet.id, + })); + return { ...tweet, isExistingQuest }; + }); + const recentTweets = await Promise.all(promises); + logger.info(`Found ${recentTweets.length} posts for ${pool.sub} in ${pool.settings.title}`); + + const newTweets = recentTweets.filter( + (tweet) => !tweet.isExistingQuest && tweet.text.includes(`#${hashtag}`), + ); + if (!newTweets.length) { + logger.info(`Found no new autoquests for ${pool.sub} in ${pool.settings.title}`); + continue; + } + logger.info(`Found ${newTweets.length} new autoquest for ${pool.sub} in ${pool.settings.title}`); + + const quests = await Promise.all( + newTweets.map(async (tweet) => { + try { + const contentMetadata = JSON.stringify({ + url: `https://twitter.com/${token.metadata.username}/status/${tweet.id}`, + username: token.metadata.username, + text: tweet.text, + minFollowersCount: 0, + }); + return await QuestService.create(QuestVariant.Twitter, pool._id, { + index: 0, + title, + description, + amount, + locks, + kind: AccessTokenKind.Twitter, + interaction: QuestSocialRequirement.TwitterLikeRetweet, + content: tweet.id, + contentMetadata, + isPublished, + }); + } catch (error) { + logger.error(error.message); + } + }), + ); + + const subject = `Created ${quests.length} Twitter Quest${quests.length && 's'}!`; + const message = `We have detected ${quests.length} new tweet${ + quests.length && 's' + } in <a href="https://www.twitter.com/${token.metadata.username}">@${ + token.metadata.username + }</a>. A Twitter Quest ${quests.length && 'for each'} has been ${ + isPublished ? 'published' : 'prepared' + } for you in <a href="${DASHBOARD_URL}/pool/${pool._id}/quests">${pool.settings.title}</a>.`; + + await MailService.send(account.email, subject, message); + + logger.info(`Created ${quests.length} Twitter Quests in ${pool.settings.title}`); + } catch (error) { + logger.info(error); + } + } +} diff --git a/apps/api/src/app/jobs/sendPoolAnalyticsReport.ts b/apps/api/src/app/jobs/sendPoolAnalyticsReport.ts new file mode 100644 index 000000000..ad1c04109 --- /dev/null +++ b/apps/api/src/app/jobs/sendPoolAnalyticsReport.ts @@ -0,0 +1,110 @@ +import { Pool } from '@thxnetwork/api/models'; +import { DASHBOARD_URL } from '../config/secrets'; +import { logger } from '../util/logger'; +import PoolService from '../services/PoolService'; +import AccountProxy from '../proxies/AccountProxy'; +import MailService from '../services/MailService'; +import AnalyticsService from '../services/AnalyticsService'; + +const emojiMap = ['🥇', '🥈', '🥉']; +const oneDay = 86400000; // one day in milliseconds + +export async function sendPoolAnalyticsReport() { + const endDate = new Date(); + endDate.setHours(0, 0, 0, 0); + + const startDate = new Date(new Date(endDate).getTime() - oneDay * 7); + const dateRange = { startDate, endDate }; + + let account: TAccount; + + for await (const pool of Pool.find({ 'settings.isWeeklyDigestEnabled': true })) { + try { + if (!account || account.sub != pool.sub) account = await AccountProxy.findById(pool.sub); + if (!account.email) continue; + + const { dailyQuest, inviteQuest, socialQuest, customQuest, coinReward, nftReward } = + await AnalyticsService.getPoolMetrics(pool, dateRange); + const leaderboard = await PoolService.findParticipants(pool, 1, 10); + const subs = leaderboard.results.map((entry) => entry.sub); + const accounts = await AccountProxy.find({ subs }); + + const totalPointsClaimed = + dailyQuest.totalAmount + inviteQuest.totalAmount + socialQuest.totalAmount + customQuest.totalAmount; + const totalPointsSpent = coinReward.totalAmount + nftReward.totalAmount; + + // Skip if nothing happened. + if (!totalPointsClaimed && !totalPointsSpent) continue; + + let html = `<p style="font-size: 18px">Hi there!👋</p>`; + html += `<p>We're pleased to bring you the <strong>Weekly Digest</strong> for "${pool.settings.title}".</p>`; + html += `<hr />`; + + html += `<p><strong>🏆 Quests: </strong> ${totalPointsClaimed} points claimed</p>`; + html += `<table width="100%" role="presentation" border="0" cellpadding="0" cellspacing="0">`; + if (dailyQuest.totalCreated) { + html += `<tr> + <td><strong>${dailyQuest.totalCreated}x</strong> Daily - ${dailyQuest.totalAmount} pts</td> + <td align="right"><a href="${DASHBOARD_URL}/pool/${pool._id}/quests">Manage</a></td> + `; + } + if (inviteQuest.totalCreated) { + html += `<tr> + <td><strong>${inviteQuest.totalCreated}x</strong> Invite - ${inviteQuest.totalAmount} pts)</td> + <td align="right"><a href="${DASHBOARD_URL}/pool/${pool._id}/quests">Manage</a></td> + </tr>`; + } + if (socialQuest.totalCreated) { + html += `<tr> + <td><strong>${socialQuest.totalCreated}x</strong> Social - ${socialQuest.totalAmount} pts</td> + <td align="right"><a href="${DASHBOARD_URL}/pool/${pool._id}/quests">Manage</a></td> + </tr>`; + } + if (customQuest.totalCreated) { + html += `<tr> + <td><strong>${customQuest.totalCreated}x</strong> Custom - ${customQuest.totalAmount} pts</td> + <td align="right"><a href="${DASHBOARD_URL}/pool/${pool._id}/quests">Manage</a></td> + </tr>`; + } + html += `</table>`; + html += `<hr />`; + + html += `<p><strong>🎁 Rewards: </strong> ${totalPointsSpent} points spent</p>`; + html += `<table width="100%" role="presentation" border="0" cellpadding="0" cellspacing="0">`; + if (coinReward.totalCreated) { + html += `<tr> + <td><strong>${coinReward.totalCreated}x</strong> Coin Rewards (${coinReward.totalAmount} points)</td> + <td align="right" ><a href="${DASHBOARD_URL}/pool/${pool._id}/rewards">Manage</a></td> + </tr>`; + } + if (nftReward.totalCreated) { + html += `<tr> + <td><strong>${nftReward.totalCreated}x</strong> NFT Rewards (${nftReward.totalAmount} points)</td> + <td align="right"><a href="${DASHBOARD_URL}/pool/${pool._id}/rewards">Manage</a></td> + </tr>`; + } + html += `</table>`; + html += `<hr />`; + + html += `<p style="font-size:16px"><strong>Top 3</strong></p>`; + html += `<table role="presentation" border="0" cellpadding="0" cellspacing="0">`; + + for (const index in leaderboard.results) { + const entry = leaderboard[index]; + const account = accounts.find((a) => a.sub === entry.sub); + + html += `<tr> + <td width="5%">${emojiMap[index]}</td> + <td><strong>${account.firstName || '...'}</strong> ${entry.wallet.address.substring(0, 8)}...</td> + <td align="right" width="25%"><strong>${entry.score} Points</strong></td> + </tr>`; + } + html += '</table>'; + html += `<a href="${DASHBOARD_URL}/pool/${pool._id}/participants">All participants</a>`; + + await MailService.send(account.email, `🎁 Weekly Digest: "${pool.settings.title}"`, html); + } catch (error) { + logger.error(error); + } + } +} diff --git a/apps/api/src/app/jobs/updateCampaignRanks.ts b/apps/api/src/app/jobs/updateCampaignRanks.ts new file mode 100644 index 000000000..a34e1fe71 --- /dev/null +++ b/apps/api/src/app/jobs/updateCampaignRanks.ts @@ -0,0 +1,100 @@ +import { + Pool, + RewardCoin, + RewardNFT, + RewardCustom, + RewardCoupon, + RewardDiscordRole, + RewardGalachain, + QuestDaily, + QuestInvite, + QuestSocial, + QuestCustom, + QuestWeb3, + QuestGitcoin, + Participant, +} from '@thxnetwork/api/models'; +import { logger } from '../util/logger'; + +export async function updateCampaignRanks() { + try { + const questModels = [QuestDaily, QuestInvite, QuestSocial, QuestCustom, QuestWeb3, QuestGitcoin]; + const rewardModels = [RewardCoin, RewardNFT, RewardCustom, RewardCoupon, RewardDiscordRole, RewardGalachain]; + const questLookupStages = questModels.map((model) => { + return { + $lookup: { + from: model.collection.name, + localField: 'id', + foreignField: 'poolId', + as: model.collection.name, + }, + }; + }); + const rewardLookupStages = rewardModels.map((model) => { + return { + $lookup: { + from: model.collection.name, + localField: 'id', + foreignField: 'poolId', + as: model.collection.name, + }, + }; + }); + const campaigns = await Pool.aggregate([ + { + $addFields: { + id: { $toString: '$_id' }, + }, + }, + { + $lookup: { + from: Participant.collection.name, + localField: 'id', + foreignField: 'poolId', + as: Participant.collection.name, + }, + }, + // Rewards + ...questLookupStages, + ...rewardLookupStages, + { + $addFields: { + participantCount: { $size: `$${Participant.collection.name}` }, + totalQuestCount: { + $size: { + $concatArrays: questModels.map((model) => `$${model.collection.name}`), + }, + }, + totalRewardsCount: { + $size: { + $concatArrays: rewardModels.map((model) => `$${model.collection.name}`), + }, + }, + }, + }, + { + $match: { + 'settings.isPublished': true, + 'totalQuestCount': { $gt: 0 }, + 'totalRewardsCount': { $gt: 0 }, + }, + }, + { + $sort: { participantCount: -1 }, + }, + ]).exec(); + + await Pool.bulkWrite( + campaigns.map((campaign, index) => { + return { + updateOne: { + filter: { _id: campaign._id }, + update: { $set: { rank: Number(index) + 1 } }, + }, + }; + }), + ); + } catch (error) { + logger.error(error); + } +} diff --git a/apps/api/src/app/jobs/updateParticipantRanks.ts b/apps/api/src/app/jobs/updateParticipantRanks.ts new file mode 100644 index 000000000..94da0c021 --- /dev/null +++ b/apps/api/src/app/jobs/updateParticipantRanks.ts @@ -0,0 +1,20 @@ +import { Pool } from '@thxnetwork/api/models'; +import { logger } from '../util/logger'; +import { Job } from '@hokify/agenda'; +import AnalyticsService from '../services/AnalyticsService'; + +export async function updateParticipantRanks(job: Job) { + if (!job.attrs.data) return; + + try { + const { poolId } = job.attrs.data as { poolId: string }; + const pool = await Pool.findById(poolId); + if (!pool) throw new Error('Could not find campaign'); + + await AnalyticsService.createLeaderboard(pool); + + logger.info('Updated participant ranks.'); + } catch (error) { + logger.error(error); + } +} diff --git a/apps/api/src/app/jobs/updatePendingTransactions.ts b/apps/api/src/app/jobs/updatePendingTransactions.ts new file mode 100644 index 000000000..337cf18b8 --- /dev/null +++ b/apps/api/src/app/jobs/updatePendingTransactions.ts @@ -0,0 +1,56 @@ +import { TransactionState, TransactionType } from '@thxnetwork/common/enums'; +import { Transaction, TransactionDocument } from '@thxnetwork/api/models/Transaction'; +import { Wallet } from '../models/Wallet'; +import TransactionService from '@thxnetwork/api/services/TransactionService'; +import SafeService from '../services/SafeService'; +import { logger } from '../util/logger'; + +export async function updatePendingTransactions() { + const transactions: TransactionDocument[] = await Transaction.find({ + $or: [{ state: TransactionState.Confirmed }, { state: TransactionState.Sent }], + }).sort({ createdAt: 'asc' }); + + // Iterate over all tx sent to or proposed and confirmed by the relayer + for (const tx of transactions) { + switch (tx.state) { + // Legacy tx will not have this state + // Transactions is proposed and confirmed by the relayer, awaiting user wallet confirmation + case TransactionState.Confirmed: { + if (!tx.walletId) continue; + + const wallet = await Wallet.findById(tx.walletId); + + let pendingTx; + try { + pendingTx = await SafeService.getTransaction(wallet, tx.safeTxHash); + logger.debug(`Safe TX Found: ${tx.safeTxHash}`); + } catch (error) { + logger.error(error); + } + + // Safes for pools have a single signer (relayer) while safes for end users + // have 2 (relayer + web3auth mpc key) + const threshold = wallet.poolId ? 1 : 2; + if (pendingTx && pendingTx.confirmations.length >= threshold) { + logger.debug(`Safe TX Confirmed: ${tx.safeTxHash}`); + + try { + await SafeService.executeTransaction(wallet, tx.safeTxHash); + logger.debug(`Safe TX Executed: ${tx.safeTxHash}`); + } catch (error) { + await tx.updateOne({ state: TransactionState.Failed }); + logger.error(error); + } + } + break; + } + // TransactionType.Default is handled in tx service send methods + case TransactionState.Sent: { + if (tx.type == TransactionType.Relayed) { + TransactionService.queryTransactionStatusDefender(tx).catch((error) => logger.error(error)); + } + break; + } + } + } +} diff --git a/apps/api/src/app/middlewares/assertAccount.ts b/apps/api/src/app/middlewares/assertAccount.ts new file mode 100644 index 000000000..f63167119 --- /dev/null +++ b/apps/api/src/app/middlewares/assertAccount.ts @@ -0,0 +1,21 @@ +import { Response, Request, NextFunction } from 'express'; +import { NotFoundError } from '../util/errors'; +import { Pool } from '@thxnetwork/api/models'; +import AccountProxy from '../proxies/AccountProxy'; + +const assertAccount = async (req: Request, res: Response, next: NextFunction) => { + const account = await AccountProxy.findById(req.auth.sub); + if (!account) throw new Error('Account not found.'); + req.account = account; + + const poolId = req.header('X-PoolId'); + if (poolId) { + const pool = await Pool.findById(poolId); + if (!pool) throw new NotFoundError('Could not find campaign'); + req.campaign = pool; + } + + next(); +}; + +export { assertAccount }; diff --git a/apps/api/src/app/middlewares/assertPayment.ts b/apps/api/src/app/middlewares/assertPayment.ts new file mode 100644 index 000000000..27aca009d --- /dev/null +++ b/apps/api/src/app/middlewares/assertPayment.ts @@ -0,0 +1,26 @@ +import { Response, Request, NextFunction } from 'express'; +import { BigNumber } from 'alchemy-sdk'; +import { logger } from '../util/logger'; +import SafeService from '../services/SafeService'; +import PoolService from '../services/PoolService'; +import PaymentService from '../services/PaymentService'; + +/* + * This middleware function is used to assert payments of the pool owner. + * @dev Assumes that the poolId is available as 'id' param in the request + */ +export async function assertPayment(req: Request, res: Response, next: NextFunction) { + const pool = await PoolService.getById(req.params.id); + const safe = await SafeService.findOneByPool(pool); + const balanceInWei = await PaymentService.balanceOf(safe); + + // If pool.createdAt + 2 weeks is larger than now there should be a payment + const isPostTrial = Date.now() > new Date(pool.trialEndsAt).getTime(); + if (isPostTrial && BigNumber.from(balanceInWei).eq(0)) { + // @dev Disable until we agree on a better notification flow + // throw new ForbiddenError('Payment is required.'); + logger.info(JSON.stringify({ poolId: pool._id, safeAddress: safe.address, isPostTrial, balanceInWei })); + } + + next(); +} diff --git a/apps/api/src/app/middlewares/assertPoolAccess.ts b/apps/api/src/app/middlewares/assertPoolAccess.ts new file mode 100644 index 000000000..d4d3bb93d --- /dev/null +++ b/apps/api/src/app/middlewares/assertPoolAccess.ts @@ -0,0 +1,25 @@ +import { Response, Request, NextFunction } from 'express'; +import PoolService from '@thxnetwork/api/services/PoolService'; +import { AudienceUnauthorizedError, ForbiddenError, SubjectUnauthorizedError } from '@thxnetwork/api/util/errors'; +import { ALLOWED_API_CLIENT_ID } from '../config/secrets'; + +export async function assertPoolAccess( + req: Request & { user: { sub: string; aud: string } }, + res: Response, + next: NextFunction, +) { + if (req.auth.aud === ALLOWED_API_CLIENT_ID && ALLOWED_API_CLIENT_ID) return next(); + + const poolId = req.header('X-PoolId') || req.params.id; // Deprecate the header non pool child resources are tested + if (!poolId) throw new ForbiddenError('Missing id param or X-PoolId header'); + + // If there is a sub check if the user is an owner or collaborator + const isSubjectAllowed = await PoolService.isSubjectAllowed(req.auth.sub, poolId); + if (req.auth.sub !== req.auth.aud && !isSubjectAllowed) throw new SubjectUnauthorizedError(); + + const isAudienceAllowed = await PoolService.isAudienceAllowed(req.auth.aud, poolId); + // Equal sub and aud are client_credentials grants + if (req.auth.sub === req.auth.aud && !isAudienceAllowed) throw new AudienceUnauthorizedError(); + + next(); +} diff --git a/apps/api/src/app/middlewares/assertQuestAccess.ts b/apps/api/src/app/middlewares/assertQuestAccess.ts new file mode 100644 index 000000000..fb9dd7378 --- /dev/null +++ b/apps/api/src/app/middlewares/assertQuestAccess.ts @@ -0,0 +1,19 @@ +import { ForbiddenError } from '@thxnetwork/api/util/errors'; +import { QuestVariant } from '@thxnetwork/common/enums'; +import { Response, Request, NextFunction } from 'express'; +import { serviceMap } from '../services/interfaces/IQuestService'; + +// @dev For all social quests use QuestVariant.Twitter by default as we can not access the +// quest interaction type yet but have to assign the db collection +// in order to check quest access +export function assertQuestAccess(variant: QuestVariant) { + return async (req: Request, res: Response, next: NextFunction) => { + const poolId = req.header('X-PoolId'); + const Quest = serviceMap[variant].models.quest; + const quest = await Quest.findById(req.params.id); + if (poolId !== quest.poolId) { + throw new ForbiddenError(`Not your quest!`); + } + next(); + }; +} diff --git a/apps/api/src/app/middlewares/assertRequestInput.ts b/apps/api/src/app/middlewares/assertRequestInput.ts new file mode 100644 index 000000000..dcc0fe481 --- /dev/null +++ b/apps/api/src/app/middlewares/assertRequestInput.ts @@ -0,0 +1,13 @@ +import { Response, Request, NextFunction } from 'express'; +import { validationResult } from 'express-validator'; + +export function assertRequestInput(validations: any) { + return async (req: Request, res: Response, next: NextFunction) => { + await Promise.all(validations.map((validation: any) => validation.run(req))); + + const errors = validationResult(req); + if (errors.isEmpty()) return next(); + + res.status(400).json({ errors: errors.array() }); + }; +} diff --git a/apps/api/src/app/middlewares/assertUUID.ts b/apps/api/src/app/middlewares/assertUUID.ts new file mode 100644 index 000000000..9fa05c645 --- /dev/null +++ b/apps/api/src/app/middlewares/assertUUID.ts @@ -0,0 +1,11 @@ +import { Request } from 'express'; +import { ForbiddenError } from '../util/errors'; +import WalletService from '../services/WalletService'; + +const assertUUID = async (req: Request) => { + const sub = await WalletService.findOne({ uuid: req.body.uuid }); + if (!sub) throw new ForbiddenError('Sub not found for this uuid.'); + req.auth = { sub }; +}; + +export { assertUUID }; diff --git a/apps/api/src/app/middlewares/assertWallet.ts b/apps/api/src/app/middlewares/assertWallet.ts new file mode 100644 index 000000000..5585dbdbd --- /dev/null +++ b/apps/api/src/app/middlewares/assertWallet.ts @@ -0,0 +1,19 @@ +import { Response, Request, NextFunction } from 'express'; +import { ForbiddenError, NotFoundError } from '../util/errors'; +import WalletService from '../services/WalletService'; + +const assertWallet = async (req: Request, res: Response, next: NextFunction) => { + const walletId = req.query.walletId as string; + + if (walletId) { + const wallet = await WalletService.findById(walletId); + if (!wallet) throw new NotFoundError('Wallet not found'); + if (wallet.sub !== req.auth.sub) throw new ForbiddenError('Wallet not owned by sub.'); + + req.wallet = wallet; + } + + next(); +}; + +export { assertWallet }; diff --git a/apps/api/src/app/middlewares/checkJwt.ts b/apps/api/src/app/middlewares/checkJwt.ts new file mode 100644 index 000000000..7a12f3d04 --- /dev/null +++ b/apps/api/src/app/middlewares/checkJwt.ts @@ -0,0 +1,20 @@ +import jwksRsa from 'jwks-rsa'; +import expressJwtPermissions from 'express-jwt-permissions'; +import { expressjwt } from 'express-jwt'; +import { AUTH_URL } from '@thxnetwork/api/config/secrets'; + +export const checkJwt: any = expressjwt({ + secret: jwksRsa.expressJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 10, + jwksUri: `${AUTH_URL}/jwks`, + }), + issuer: AUTH_URL, + algorithms: ['RS256'], +}); + +export const guard: any = expressJwtPermissions({ + requestProperty: 'auth', + permissionsProperty: 'scope', +}); diff --git a/apps/api/src/app/middlewares/corsHandler.ts b/apps/api/src/app/middlewares/corsHandler.ts new file mode 100644 index 000000000..482968f9c --- /dev/null +++ b/apps/api/src/app/middlewares/corsHandler.ts @@ -0,0 +1,29 @@ +import cors from 'cors'; +import { AUTH_URL, API_URL, WALLET_URL, DASHBOARD_URL, WIDGET_URL, PUBLIC_URL } from '@thxnetwork/api/config/secrets'; +import ClientProxy from '@thxnetwork/api/proxies/ClientProxy'; + +export const corsHandler = cors(async (req: any, callback: any) => { + const origin = req.header('Origin'); + const allowedOrigins = [ + AUTH_URL, + API_URL, + WALLET_URL, + DASHBOARD_URL, + WIDGET_URL, + PUBLIC_URL, + 'https://app.thx.network', + 'https://dev-app.thx.network', + ]; + const isAllowedOrigin = await ClientProxy.isAllowedOrigin(origin); + + if (isAllowedOrigin) { + allowedOrigins.push(origin); + } + + if (!origin || allowedOrigins.includes(origin)) { + allowedOrigins.push(origin); + callback(null, { credentials: true, origin: allowedOrigins }); + } else { + callback(new Error(`${origin} is not allowed by CORS`)); + } +}); diff --git a/apps/api/src/app/middlewares/errorLogger.ts b/apps/api/src/app/middlewares/errorLogger.ts new file mode 100644 index 000000000..e51bb8bc9 --- /dev/null +++ b/apps/api/src/app/middlewares/errorLogger.ts @@ -0,0 +1,11 @@ +import { THXHttpError } from '@thxnetwork/api/util/errors'; +import { logger } from '@thxnetwork/api/util/logger'; +import { NextFunction, Request, Response } from 'express'; + +export const errorLogger = (error: Error, req: Request, res: Response, next: NextFunction) => { + if (!(error instanceof THXHttpError)) { + logger.error('Error caught:', error); + } + + next(error); +}; diff --git a/apps/api/src/app/middlewares/errorNormalizer.ts b/apps/api/src/app/middlewares/errorNormalizer.ts new file mode 100644 index 000000000..fd2308ecf --- /dev/null +++ b/apps/api/src/app/middlewares/errorNormalizer.ts @@ -0,0 +1,11 @@ +import { NextFunction, Request, Response } from 'express'; +import { UnauthorizedError } from '@thxnetwork/api/util/errors'; +import { UnauthorizedError as JWTUnauthorizedError } from 'express-jwt'; + +export const errorNormalizer = (error: Error, _req: Request, _res: Response, next: NextFunction) => { + if (error instanceof JWTUnauthorizedError) { + return next(new UnauthorizedError(error.message)); + } + + next(error); +}; diff --git a/apps/api/src/app/middlewares/errorOutput.ts b/apps/api/src/app/middlewares/errorOutput.ts new file mode 100644 index 000000000..72ad710d9 --- /dev/null +++ b/apps/api/src/app/middlewares/errorOutput.ts @@ -0,0 +1,28 @@ +import { NextFunction, Request, Response } from 'express'; +import { THXHttpError } from '@thxnetwork/api/util/errors'; +import { NODE_ENV } from '@thxnetwork/api/config/secrets'; + +interface ErrorResponse { + error: { + message: string; + error?: Error; + rootMessage?: string; + stack?: string; + }; +} + +// Error handler needs to have 4 arguments. +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const errorOutput = (error: any, req: Request, res: Response, next: NextFunction) => { + let status = 500; + const response: ErrorResponse = { error: { message: 'Unable to fulfill request' } }; + if (error instanceof THXHttpError || error.status) { + status = error.status; + response.error.message = error.message; + } else if (NODE_ENV !== 'production') { + response.error.error = error; + response.error.stack = error.stack; + } + + res.status(status).json(response); +}; diff --git a/apps/api/src/app/middlewares/index.ts b/apps/api/src/app/middlewares/index.ts new file mode 100644 index 000000000..5d0f6b48d --- /dev/null +++ b/apps/api/src/app/middlewares/index.ts @@ -0,0 +1,12 @@ +export * from './assertPoolAccess'; +export * from './assertRequestInput'; +export * from './errorOutput'; +export * from './errorLogger'; +export * from './errorNormalizer'; +export * from './notFoundHandler'; +export * from './corsHandler'; +export * from './checkJwt'; +export * from './assertPayment'; +export * from './assertQuestAccess'; +export * from './assertAccount'; +export * from './assertWallet'; diff --git a/apps/api/src/app/middlewares/morgan.ts b/apps/api/src/app/middlewares/morgan.ts new file mode 100644 index 000000000..994de436b --- /dev/null +++ b/apps/api/src/app/middlewares/morgan.ts @@ -0,0 +1,16 @@ +import morgan from 'morgan'; +import { logger } from '../util/logger'; +import { NODE_ENV } from '../config/secrets'; + +const stream = { + write: (message) => logger.http(message), +}; + +const skip = () => { + return ['development', 'test'].includes(NODE_ENV || 'development'); +}; + +export default morgan( + ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" - :response-time ms', + { stream, skip }, +); diff --git a/apps/api/src/app/middlewares/notFoundHandler.ts b/apps/api/src/app/middlewares/notFoundHandler.ts new file mode 100644 index 000000000..95b5beb28 --- /dev/null +++ b/apps/api/src/app/middlewares/notFoundHandler.ts @@ -0,0 +1,6 @@ +import { NextFunction, Request, Response } from 'express'; +import { NotFoundError } from '@thxnetwork/api/util/errors'; + +export const notFoundHandler = (req: Request, res: Response, next: NextFunction) => { + next(new NotFoundError()); +}; diff --git a/apps/api/src/app/migrations/20240321144151-galachain-pkey.js b/apps/api/src/app/migrations/20240321144151-galachain-pkey.js new file mode 100644 index 000000000..ebae8457a --- /dev/null +++ b/apps/api/src/app/migrations/20240321144151-galachain-pkey.js @@ -0,0 +1,24 @@ +const { ethers } = require('ethers'); + +module.exports = { + async up(db, client) { + const poolsColl = db.collection('pool'); + const pools = await (await poolsColl.find({})).toArray(); + const operations = pools.map((pool) => { + const privateKey = ethers.Wallet.createRandom().privateKey; + return { + updateOne: { + filter: { _id: pool._id }, + update: { + $set: { 'settings.galachainPrivateKey': privateKey }, + }, + }, + }; + }); + await poolsColl.bulkWrite(operations); + }, + + async down(db, client) { + // + }, +}; diff --git a/apps/api/src/app/migrations/20240329123915-quest-metadata.js b/apps/api/src/app/migrations/20240329123915-quest-metadata.js new file mode 100644 index 000000000..8b9c03556 --- /dev/null +++ b/apps/api/src/app/migrations/20240329123915-quest-metadata.js @@ -0,0 +1,29 @@ +module.exports = { + async up(db, client) { + await db.collection('questdailyentry').updateMany( + {}, + { + $rename: { + ip: 'metadata.ip', + }, + }, + ); + await db.collection('questsocialentry').updateMany( + {}, + { + $rename: { + platformUserId: 'metadata.platformUserId', + publicMetrics: 'metadata.twitter', + }, + }, + ); + await db.collection('questweb3entry').updateMany({}, { $rename: { address: 'metadata.address' } }); + await db.collection('questgitcoinentry').updateMany({}, { $rename: { address: 'metadata.address' } }); + }, + + async down(db, client) { + // TODO write the statements to rollback your migration (if possible) + // Example: + // await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: false}}); + }, +}; diff --git a/apps/api/src/app/migrations/20240430124026-message-query.js b/apps/api/src/app/migrations/20240430124026-message-query.js new file mode 100644 index 000000000..b81f2abad --- /dev/null +++ b/apps/api/src/app/migrations/20240430124026-message-query.js @@ -0,0 +1,84 @@ +const { ObjectId } = require('mongodb'); + +const createQuery = (operators) => { + const operatorKeys = Object.keys(operators); + if (!operatorKeys) return; + const media = !operators['media'] || ['ignore', null].includes(operators['media']) ? '' : ` ${operators['media']}`; + const query = operatorKeys + .map((key) => { + switch (key) { + case 'from': { + const items = operators[key]; + if (!items) return; + const authors = items.map((author) => `from:${author}`).join(' OR '); + return items.length > 1 ? `(${authors})` : authors; + } + case 'to': { + const items = operators[key]; + if (!items) return; + const authors = items.map((author) => `to:${author}`).join(' OR '); + return items.length > 1 ? `(${authors})` : authors; + } + case 'text': { + const items = operators[key]; + if (!items) return; + const texts = items.map((value) => `"${value}"`).join(' OR '); + return items.length > 1 ? `(${texts})` : texts; + } + case 'url': { + const items = operators[key]; + if (!items) return; + const urls = items.map((value) => `url:${value}`).join(' OR '); + return items.length > 1 ? `(${urls})` : urls; + } + case 'hashtags': { + const items = operators[key]; + if (!items) return; + const hashtags = items.map((tag) => `#${tag}`).join(' OR '); + return (items.length > 1 ? `(${hashtags})` : hashtags) + media; + } + case 'mentions': { + const items = operators[key]; + if (!items) return; + const mentions = items.map((tag) => `@${tag}`).join(' OR '); + return (items.length > 1 ? `(${mentions})` : mentions) + media; + } + } + return; + }) + .filter((query) => !!query) + .join(' '); + + return `${query} -is:retweet`; +}; + +module.exports = { + async up(db, client) { + const questColl = db.collection('questsocial'); + const quests = await questColl.find({ interaction: 6 }).toArray(); + + for (const quest of quests) { + if (!quest.contentMetadata) continue; + try { + const metadata = JSON.parse(quest.contentMetadata); + const operators = { + text: [quest.content], + }; + const query = createQuery(operators); + const contentMetadata = JSON.stringify({ + ...metadata, + query, + operators, + }); + + await questColl.updateOne({ _id: quest._id }, { $set: { content: query, contentMetadata } }); + } catch (error) { + console.log(error); + } + } + }, + + async down(db, client) { + // + }, +}; diff --git a/apps/api/src/app/models/Brand.ts b/apps/api/src/app/models/Brand.ts new file mode 100644 index 000000000..c8ebb880f --- /dev/null +++ b/apps/api/src/app/models/Brand.ts @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; + +export type TBrandUpdate = Partial<TBrand>; + +export const Brand = mongoose.model<TBrand>( + 'Brand', + new mongoose.Schema({ + poolId: String, + previewImgUrl: String, + logoImgUrl: String, + backgroundImgUrl: String, + widgetPreviewImgUrl: String, + }), + 'brand', +); diff --git a/apps/api/src/app/models/Client.ts b/apps/api/src/app/models/Client.ts new file mode 100644 index 000000000..dd98c556c --- /dev/null +++ b/apps/api/src/app/models/Client.ts @@ -0,0 +1,42 @@ +import mongoose from 'mongoose'; + +export type TClient = { + sub: string; + name: string; + poolId: string; + grantType: string; + clientId: string; + clientSecret: string; + requestUris: string[]; + registrationAccessToken: string; + origins?: string[]; +}; +export type TClientPayload = { + application_type: string; + grant_types: string[]; + scope: string; + response_types: string[]; + request_uris?: string[]; + redirect_uris?: string[]; + post_logout_redirect_uris?: string[]; +}; + +export type ClientDocument = mongoose.Document & TClient; + +export const Client = mongoose.model<ClientDocument>( + 'Client', + new mongoose.Schema( + { + sub: String, + name: String, + poolId: String, + clientId: String, + clientSecret: String, + grantType: String, + registrationAccessToken: String, + origins: [String], + }, + { timestamps: true }, + ), + 'client', +); diff --git a/apps/api/src/app/models/Collaborator.ts b/apps/api/src/app/models/Collaborator.ts new file mode 100644 index 000000000..160aed12a --- /dev/null +++ b/apps/api/src/app/models/Collaborator.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; + +export type CollaboratorDocument = mongoose.Document & TCollaborator; + +export const Collaborator = mongoose.model<CollaboratorDocument>( + 'Collaborator', + new mongoose.Schema( + { + sub: String, + poolId: String, + email: String, + uuid: String, + state: Number, + }, + { timestamps: true }, + ), + 'collaborator', +); diff --git a/apps/api/src/app/models/CouponCode.ts b/apps/api/src/app/models/CouponCode.ts new file mode 100644 index 000000000..885bc539a --- /dev/null +++ b/apps/api/src/app/models/CouponCode.ts @@ -0,0 +1,17 @@ +import mongoose from 'mongoose'; + +export type CouponCodeDocument = mongoose.Document & TCouponCode; + +export const CouponCode = mongoose.model<CouponCodeDocument>( + 'CouponCode', + new mongoose.Schema( + { + poolId: String, + couponRewardId: String, + code: String, + sub: String, + }, + { timestamps: true }, + ), + 'couponcode', +); diff --git a/apps/api/src/app/models/DiscordGuild.ts b/apps/api/src/app/models/DiscordGuild.ts new file mode 100644 index 000000000..0d3a3b27f --- /dev/null +++ b/apps/api/src/app/models/DiscordGuild.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; + +export type DiscordGuildDocument = mongoose.Document & TDiscordGuild; + +export const DiscordGuild = mongoose.model<DiscordGuildDocument>( + 'DiscordGuild', + new mongoose.Schema( + { + sub: String, + poolId: String, + guildId: String, + channelId: String, + adminRoleId: String, + name: String, + secret: String, + }, + { + timestamps: true, + }, + ), + 'discordguild', +); diff --git a/apps/api/src/app/models/DiscordMessage.ts b/apps/api/src/app/models/DiscordMessage.ts new file mode 100644 index 000000000..f6bef8dc4 --- /dev/null +++ b/apps/api/src/app/models/DiscordMessage.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; + +export type DiscordMessageDocument = mongoose.Document & TDiscordMessage; + +export const DiscordMessage = mongoose.model<DiscordMessageDocument>( + 'DiscordMessage', + new mongoose.Schema( + { + guildId: String, + messageId: String, + memberId: String, + }, + { + timestamps: true, + }, + ), + 'discordmessage', +); diff --git a/apps/api/src/app/models/DiscordReaction.ts b/apps/api/src/app/models/DiscordReaction.ts new file mode 100644 index 000000000..fda975859 --- /dev/null +++ b/apps/api/src/app/models/DiscordReaction.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type DiscordReactionDocument = mongoose.Document & TDiscordReaction; + +export const DiscordReaction = mongoose.model<DiscordReactionDocument>( + 'DiscordReaction', + new mongoose.Schema( + { + guildId: String, + messageId: String, + memberId: String, + content: String, + }, + { + timestamps: true, + }, + ), + 'discordreaction', +); diff --git a/apps/api/src/app/models/DiscordUser.ts b/apps/api/src/app/models/DiscordUser.ts new file mode 100644 index 000000000..7c24def95 --- /dev/null +++ b/apps/api/src/app/models/DiscordUser.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; + +export type DiscordUserDocument = mongoose.Document & TDiscordUser; + +export const DiscordUser = mongoose.model<DiscordUserDocument>( + 'DiscordUser', + new mongoose.Schema( + { + userId: String, + guildId: String, + profileImgUrl: String, + username: String, + publicMetrics: { + joinedAt: Date, + messageCount: Number, + reactionCount: Number, + }, + }, + { timestamps: true }, + ), + 'discorduser', +); diff --git a/apps/api/src/app/models/ERC1155.ts b/apps/api/src/app/models/ERC1155.ts new file mode 100644 index 000000000..bf461c98a --- /dev/null +++ b/apps/api/src/app/models/ERC1155.ts @@ -0,0 +1,30 @@ +import mongoose from 'mongoose'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { getArtifact } from '../hardhat'; + +export type ERC1155Document = mongoose.Document & TERC1155; + +const schema = new mongoose.Schema( + { + variant: String, + chainId: Number, + sub: String, + name: String, + description: String, + transactions: [String], + address: String, + baseURL: String, + archived: Boolean, + logoImgUrl: String, + }, + { timestamps: true }, +); + +schema.virtual('contract').get(function () { + if (!this.address) return; + const { web3, defaultAccount } = getProvider(this.chainId); + const { abi } = getArtifact('THXERC1155'); + return new web3.eth.Contract(abi, this.address, { from: defaultAccount }); +}); + +export const ERC1155 = mongoose.model<ERC1155Document>('ERC1155', schema, 'erc1155'); diff --git a/apps/api/src/app/models/ERC1155Metadata.ts b/apps/api/src/app/models/ERC1155Metadata.ts new file mode 100644 index 000000000..989e1ca42 --- /dev/null +++ b/apps/api/src/app/models/ERC1155Metadata.ts @@ -0,0 +1,20 @@ +import mongoose from 'mongoose'; + +export type ERC1155MetadataDocument = mongoose.Document & TERC1155Metadata; + +export const ERC1155Metadata = mongoose.model<ERC1155MetadataDocument>( + 'ERC1155Metadata', + new mongoose.Schema( + { + erc1155Id: String, + imageUrl: String, + name: String, + image: String, + description: String, + externalUrl: String, + tokenId: Number, + }, + { timestamps: true }, + ), + 'erc1155metadata', +); diff --git a/apps/api/src/app/models/ERC1155Token.ts b/apps/api/src/app/models/ERC1155Token.ts new file mode 100644 index 000000000..fac51b990 --- /dev/null +++ b/apps/api/src/app/models/ERC1155Token.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; + +export type ERC1155TokenDocument = mongoose.Document & TERC1155Token; + +export const ERC1155Token = mongoose.model<ERC1155TokenDocument>( + 'ERC1155Token', + new mongoose.Schema( + { + sub: String, + state: Number, + erc1155Id: String, + metadataId: String, + tokenId: Number, + tokenUri: String, + recipient: String, + transactions: [String], + walletId: { type: String, index: 'hashed' }, + }, + { timestamps: true }, + ), + 'erc1155token', +); diff --git a/apps/api/src/app/models/ERC20.ts b/apps/api/src/app/models/ERC20.ts new file mode 100644 index 000000000..90c635f5c --- /dev/null +++ b/apps/api/src/app/models/ERC20.ts @@ -0,0 +1,38 @@ +import mongoose from 'mongoose'; +import { ERC20Type } from '@thxnetwork/common/enums'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { getArtifact, TContractName } from '../hardhat'; + +export type ERC20Document = mongoose.Document & TERC20; + +const schema = new mongoose.Schema( + { + sub: String, + type: Number, + address: String, + chainId: Number, + name: String, + symbol: String, + transactions: [String], + archived: Boolean, + logoImgUrl: String, + }, + { timestamps: true }, +); + +schema.virtual('contractName').get(function () { + return getContractName(this.type) as TContractName; +}); + +schema.virtual('contract').get(function () { + if (!this.address) return; + const { web3, defaultAccount } = getProvider(this.chainId); + const { abi } = getArtifact(getContractName(this.type)); + return new web3.eth.Contract(abi, this.address, { from: defaultAccount }); +}); + +function getContractName(type: ERC20Type) { + return type === ERC20Type.Unlimited ? 'THXERC20_UnlimitedSupply' : 'THXERC20_LimitedSupply'; +} + +export const ERC20 = mongoose.model<ERC20Document>('ERC20', schema, 'erc20'); diff --git a/apps/api/src/app/models/ERC20Token.ts b/apps/api/src/app/models/ERC20Token.ts new file mode 100644 index 000000000..a26153c2c --- /dev/null +++ b/apps/api/src/app/models/ERC20Token.ts @@ -0,0 +1,16 @@ +import mongoose from 'mongoose'; + +export type ERC20TokenDocument = mongoose.Document & TERC20Token; + +export const ERC20Token = mongoose.model<ERC20TokenDocument>( + 'ERC20Token', + new mongoose.Schema( + { + sub: String, + erc20Id: String, + walletId: { type: String, index: 'hashed' }, + }, + { timestamps: true }, + ), + 'erc20token', +); diff --git a/apps/api/src/app/models/ERC20Transfer.ts b/apps/api/src/app/models/ERC20Transfer.ts new file mode 100644 index 000000000..ab4a80301 --- /dev/null +++ b/apps/api/src/app/models/ERC20Transfer.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type ERC20TransferDocument = mongoose.Document & TERC20Transfer; + +export const ERC20Transfer = mongoose.model<ERC20TransferDocument>( + 'ERC20Transfer', + new mongoose.Schema( + { + erc20Id: String, + from: String, + to: String, + chainId: Number, + transactionId: String, + sub: String, + }, + { timestamps: true }, + ), + 'erc20transfer', +); diff --git a/apps/api/src/app/models/ERC721.ts b/apps/api/src/app/models/ERC721.ts new file mode 100644 index 000000000..91945cff0 --- /dev/null +++ b/apps/api/src/app/models/ERC721.ts @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { getArtifact } from '../hardhat'; + +export type ERC721Document = mongoose.Document & TERC721; + +const schema = new mongoose.Schema( + { + chainId: Number, + sub: String, + name: String, + symbol: String, + description: String, + transactions: [String], + address: String, + baseURL: String, + archived: Boolean, + logoImgUrl: String, + variant: String, + }, + { timestamps: true }, +); + +schema.virtual('contract').get(function () { + if (!this.address) return; + const { web3 } = getProvider(this.chainId); + const { abi } = getArtifact('THXERC721'); + return new web3.eth.Contract(abi, this.address); +}); + +export const ERC721 = mongoose.model<ERC721Document>('ERC721', schema, 'erc721'); diff --git a/apps/api/src/app/models/ERC721Metadata.ts b/apps/api/src/app/models/ERC721Metadata.ts new file mode 100644 index 000000000..9eba455b4 --- /dev/null +++ b/apps/api/src/app/models/ERC721Metadata.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type ERC721MetadataDocument = mongoose.Document & TERC721Metadata; + +export const ERC721Metadata = mongoose.model<ERC721MetadataDocument>( + 'ERC721Metadata', + new mongoose.Schema( + { + erc721Id: String, + imageUrl: String, + name: String, + image: String, + description: String, + externalUrl: String, + }, + { timestamps: true }, + ), + 'erc721metadata', +); diff --git a/apps/api/src/app/models/ERC721Token.ts b/apps/api/src/app/models/ERC721Token.ts new file mode 100644 index 000000000..fdee045bb --- /dev/null +++ b/apps/api/src/app/models/ERC721Token.ts @@ -0,0 +1,23 @@ +import mongoose from 'mongoose'; + +export type ERC721TokenDocument = mongoose.Document & TERC721Token; + +export const ERC721Token = mongoose.model<ERC721TokenDocument>( + 'ERC721Token', + new mongoose.Schema( + { + erc721Id: String, + walletId: String, + metadataId: String, + tokenId: Number, + sub: String, + state: Number, + tokenUri: String, + recipient: String, + failReason: String, + transactions: [String], + }, + { timestamps: true }, + ), + 'erc721token', +); diff --git a/apps/api/src/app/models/ERC721Transfer.ts b/apps/api/src/app/models/ERC721Transfer.ts new file mode 100644 index 000000000..43a47e75c --- /dev/null +++ b/apps/api/src/app/models/ERC721Transfer.ts @@ -0,0 +1,20 @@ +import mongoose from 'mongoose'; + +export type ERC721TransferDocument = mongoose.Document & TERC721Transfer; + +export const ERC721Transfer = mongoose.model<ERC721TransferDocument>( + 'ERC721Transfer', + new mongoose.Schema( + { + erc721Id: String, + erc721TokenId: String, + from: String, + to: String, + chainId: Number, + transactionId: String, + sub: String, + }, + { timestamps: true }, + ), + 'erc721transfer', +); diff --git a/apps/api/src/app/models/Event.ts b/apps/api/src/app/models/Event.ts new file mode 100644 index 000000000..cb8fc33d7 --- /dev/null +++ b/apps/api/src/app/models/Event.ts @@ -0,0 +1,16 @@ +import mongoose from 'mongoose'; + +export type EventDocument = mongoose.Document & TEvent; + +export const Event = mongoose.model<EventDocument>( + 'Event', + new mongoose.Schema( + { + identityId: String, + poolId: String, + name: String, + }, + { timestamps: true }, + ), + 'event', +); diff --git a/apps/api/src/app/models/Identity.ts b/apps/api/src/app/models/Identity.ts new file mode 100644 index 000000000..937bcb4eb --- /dev/null +++ b/apps/api/src/app/models/Identity.ts @@ -0,0 +1,16 @@ +import mongoose from 'mongoose'; + +export type IdentityDocument = mongoose.Document & TIdentity; + +export const Identity = mongoose.model<IdentityDocument>( + 'Identity', + new mongoose.Schema( + { + poolId: String, + uuid: { unique: true, type: String }, + sub: String, + }, + { timestamps: true }, + ), + 'identity', +); diff --git a/apps/api/src/app/models/Invoice.ts b/apps/api/src/app/models/Invoice.ts new file mode 100644 index 000000000..6870dbfad --- /dev/null +++ b/apps/api/src/app/models/Invoice.ts @@ -0,0 +1,24 @@ +import mongoose from 'mongoose'; + +export type InvoiceDocument = mongoose.Document & TInvoice; + +export const Invoice = mongoose.model<InvoiceDocument>( + 'Invoice', + new mongoose.Schema( + { + poolId: String, + additionalUnitCount: Number, + costPerUnit: Number, + costSubscription: Number, + costTotal: Number, + currency: String, + plan: Number, + mapCount: Number, + mapLimit: Number, + periodStartDate: Date, + periodEndDate: Date, + }, + { timestamps: true }, + ), + 'invoice', +); diff --git a/apps/api/src/app/models/Job.ts b/apps/api/src/app/models/Job.ts new file mode 100644 index 000000000..a105ce448 --- /dev/null +++ b/apps/api/src/app/models/Job.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; + +export type JobDocument = mongoose.Document & TJob; + +export const Job = mongoose.model<JobDocument>( + 'Job', + new mongoose.Schema( + { + name: String, + data: Object, + lastRunAt: Date, + failedAt: Date, + failReason: String, + }, + { timestamps: true }, + ), + 'jobs', +); diff --git a/apps/api/src/app/models/Notification.ts b/apps/api/src/app/models/Notification.ts new file mode 100644 index 000000000..81e2c040e --- /dev/null +++ b/apps/api/src/app/models/Notification.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; + +export type NotificationDocument = mongoose.Document & TNotification; + +export const Notification = mongoose.model<NotificationDocument>( + 'Notifications', + new mongoose.Schema( + { + sub: String, + subjectId: String, + poolId: String, + subject: String, + message: String, + }, + { timestamps: true }, + ), + 'notification', +); diff --git a/apps/api/src/app/models/Participant.ts b/apps/api/src/app/models/Participant.ts new file mode 100644 index 000000000..e18c1c764 --- /dev/null +++ b/apps/api/src/app/models/Participant.ts @@ -0,0 +1,21 @@ +import mongoose from 'mongoose'; + +export type ParticipantDocument = mongoose.Document & TParticipant; + +export const Participant = mongoose.model<ParticipantDocument>( + 'Participant', + new mongoose.Schema( + { + sub: String, + poolId: String, + rank: Number, + score: Number, + balance: { type: Number, default: 0 }, + questEntryCount: Number, + riskAnalysis: { score: Number, reasons: [String] }, + isSubscribed: { type: Boolean, default: false }, + }, + { timestamps: true }, + ), + 'participant', +); diff --git a/apps/api/src/app/models/Payment.ts b/apps/api/src/app/models/Payment.ts new file mode 100644 index 000000000..b6d9188c6 --- /dev/null +++ b/apps/api/src/app/models/Payment.ts @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; + +export type PaymentDocument = mongoose.Document & TPayment; + +export const Payment = mongoose.model<PaymentDocument>( + 'Payment', + new mongoose.Schema( + { + sub: String, + poolId: String, + }, + { timestamps: true }, + ), + 'payment', +); diff --git a/apps/api/src/app/models/Pool.ts b/apps/api/src/app/models/Pool.ts new file mode 100644 index 000000000..80e885ff7 --- /dev/null +++ b/apps/api/src/app/models/Pool.ts @@ -0,0 +1,52 @@ +import mongoose from 'mongoose'; +import { WIDGET_URL } from '../config/secrets'; + +export type PoolDocument = mongoose.Document & TPool; + +const schema = new mongoose.Schema( + { + sub: String, + chainId: Number, + transactions: [String], + version: String, + token: String, + signingSecret: String, + rank: Number, + safeAddress: String, + trialEndsAt: Date, + settings: { + title: String, + slug: String, + description: String, + startDate: Date, + endDate: Date, + isArchived: Boolean, + isPublished: { type: Boolean, default: false }, + isWeeklyDigestEnabled: { type: Boolean, default: true }, + isTwitterSyncEnabled: { type: Boolean, default: false }, + discordWebhookUrl: String, + galachainPrivateKey: String, + defaults: { + discordMessage: String, + conditionalRewards: { + title: String, + description: String, + amount: Number, + hashtag: String, + isPublished: { type: Boolean, default: false }, + locks: { type: [{ questId: String, variant: Number }], default: [] }, + }, + }, + authenticationMethods: [Number], + }, + }, + { timestamps: true }, +); + +schema.virtual('campaignURL').get(function (this: PoolDocument) { + const url = new URL(WIDGET_URL); + url.pathname = `/c/${this.settings.slug}`; + return url.toString(); +}); + +export const Pool = mongoose.model<PoolDocument>('Pool', schema, 'pool'); diff --git a/apps/api/src/app/models/QRCodeEntry.ts b/apps/api/src/app/models/QRCodeEntry.ts new file mode 100644 index 000000000..d02453829 --- /dev/null +++ b/apps/api/src/app/models/QRCodeEntry.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type QRCodeEntryDocument = mongoose.Document & TQRCodeEntry; + +export const QRCodeEntry = mongoose.model<QRCodeEntryDocument>( + 'QRCodeEntry', + new mongoose.Schema( + { + sub: String, + uuid: String, + redirectURL: String, + rewardId: String, + amount: Number, + claimedAt: Date, + }, + { timestamps: true }, + ), + 'qrcodeentry', +); diff --git a/apps/api/src/app/models/Quest.ts b/apps/api/src/app/models/Quest.ts new file mode 100644 index 000000000..9950b8481 --- /dev/null +++ b/apps/api/src/app/models/Quest.ts @@ -0,0 +1,13 @@ +export const questSchema = { + uuid: String, + poolId: String, + variant: Number, + title: String, + description: String, + image: String, + index: Number, + expiryDate: Date, + infoLinks: [{ label: String, url: String }], + isPublished: { type: Boolean, default: false }, + locks: { type: [{ questId: String, variant: Number }], default: [] }, +}; diff --git a/apps/api/src/app/models/QuestCustom.ts b/apps/api/src/app/models/QuestCustom.ts new file mode 100644 index 000000000..99b09ada8 --- /dev/null +++ b/apps/api/src/app/models/QuestCustom.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; +import { questSchema } from '@thxnetwork/api/models/Quest'; + +export type QuestCustomDocument = mongoose.Document & TQuestCustom; + +export const QuestCustom = mongoose.model<QuestCustomDocument>( + 'QuestCustom', + new mongoose.Schema( + { + ...(questSchema as any), + amount: Number, + limit: Number, + eventName: String, + }, + { timestamps: true }, + ), + 'questcustom', +); diff --git a/apps/api/src/app/models/QuestCustomEntry.ts b/apps/api/src/app/models/QuestCustomEntry.ts new file mode 100644 index 000000000..0c046bd34 --- /dev/null +++ b/apps/api/src/app/models/QuestCustomEntry.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type QuestCustomEntryDocument = mongoose.Document & TQuestCustomEntry; + +export const QuestCustomEntry = mongoose.model<QuestCustomEntryDocument>( + 'QuestCustomEntry', + new mongoose.Schema( + { + questId: String, + sub: String, + uuid: String, + amount: Number, + isClaimed: Boolean, + poolId: String, + }, + { timestamps: true }, + ), + 'questcustomentry', +); diff --git a/apps/api/src/app/models/QuestDaily.ts b/apps/api/src/app/models/QuestDaily.ts new file mode 100644 index 000000000..adc438142 --- /dev/null +++ b/apps/api/src/app/models/QuestDaily.ts @@ -0,0 +1,17 @@ +import mongoose from 'mongoose'; +import { questSchema } from './Quest'; + +export type QuestDailyDocument = mongoose.Document & TQuestDaily; + +export const QuestDaily = mongoose.model<QuestDailyDocument>( + 'QuestDaily', + new mongoose.Schema( + { + ...(questSchema as any), + amounts: [Number], + eventName: String, + }, + { timestamps: true }, + ), + 'questdaily', +); diff --git a/apps/api/src/app/models/QuestDailyEntry.ts b/apps/api/src/app/models/QuestDailyEntry.ts new file mode 100644 index 000000000..ef4a4c0f7 --- /dev/null +++ b/apps/api/src/app/models/QuestDailyEntry.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; + +export type QuestDailyEntryDocument = mongoose.Document & TQuestDailyEntry; + +export const QuestDailyEntry = mongoose.model<QuestDailyEntryDocument>( + 'QuestDailyEntry', + new mongoose.Schema( + { + questId: String, + sub: String, + uuid: String, + amount: Number, + poolId: String, + metadata: { + state: Number, + ip: String, + }, + }, + { timestamps: true }, + ), + 'questdailyentry', +); diff --git a/apps/api/src/app/models/QuestGitcoin.ts b/apps/api/src/app/models/QuestGitcoin.ts new file mode 100644 index 000000000..7fbf75630 --- /dev/null +++ b/apps/api/src/app/models/QuestGitcoin.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; +import { questSchema } from '@thxnetwork/api/models/Quest'; + +export type QuestGitcoinDocument = mongoose.Document & TQuestGitcoin; + +export const QuestGitcoin = mongoose.model<QuestGitcoinDocument>( + 'QuestGitcoin', + new mongoose.Schema( + { + ...(questSchema as any), + amount: Number, + scorerId: Number, + score: Number, + }, + { timestamps: true }, + ), + 'questgitcoin', +); diff --git a/apps/api/src/app/models/QuestGitcoinEntry.ts b/apps/api/src/app/models/QuestGitcoinEntry.ts new file mode 100644 index 000000000..aad3fe0c4 --- /dev/null +++ b/apps/api/src/app/models/QuestGitcoinEntry.ts @@ -0,0 +1,21 @@ +import mongoose from 'mongoose'; + +export type QuestGitcoinEntryDocument = mongoose.Document & TQuestGitcoinEntry; + +export const QuestGitcoinEntry = mongoose.model<QuestGitcoinEntryDocument>( + 'QuestGitcoinEntry', + new mongoose.Schema( + { + poolId: String, + questId: String, + sub: String, + amount: Number, + metadata: { + address: String, + score: Number, + }, + }, + { timestamps: true }, + ), + 'questgitcoinentry', +); diff --git a/apps/api/src/app/models/QuestInvite.ts b/apps/api/src/app/models/QuestInvite.ts new file mode 100644 index 000000000..4e6cd17c3 --- /dev/null +++ b/apps/api/src/app/models/QuestInvite.ts @@ -0,0 +1,20 @@ +import mongoose from 'mongoose'; +import { questSchema } from '@thxnetwork/api/models/Quest'; + +export type QuestInviteDocument = mongoose.Document & TQuestInvite; + +export const QuestInvite = mongoose.model<QuestInviteDocument>( + 'QuestInvite', + new mongoose.Schema( + { + ...(questSchema as any), + amount: Number, + pathname: String, + successUrl: String, + token: String, + isMandatoryReview: Boolean, + }, + { timestamps: true }, + ), + 'questinvite', +); diff --git a/apps/api/src/app/models/QuestInviteEntry.ts b/apps/api/src/app/models/QuestInviteEntry.ts new file mode 100644 index 000000000..363b99ca4 --- /dev/null +++ b/apps/api/src/app/models/QuestInviteEntry.ts @@ -0,0 +1,20 @@ +import mongoose from 'mongoose'; + +export type QuestInviteEntryDocument = mongoose.Document & TQuestInviteEntry; + +export const QuestInviteEntry = mongoose.model<QuestInviteEntryDocument>( + 'QuestInviteEntry', + new mongoose.Schema( + { + questId: String, + sub: String, + uuid: String, + amount: String, + isApproved: Boolean, + poolId: String, + metadata: String, + }, + { timestamps: true }, + ), + 'questinviteentry', +); diff --git a/apps/api/src/app/models/QuestSocial.ts b/apps/api/src/app/models/QuestSocial.ts new file mode 100644 index 000000000..f130cadbd --- /dev/null +++ b/apps/api/src/app/models/QuestSocial.ts @@ -0,0 +1,21 @@ +import mongoose from 'mongoose'; +import { questSchema } from './Quest'; + +export type QuestSocialDocument = mongoose.Document & TQuestSocial; + +export const QuestSocial = mongoose.model<QuestSocialDocument>( + 'QuestSocial', + new mongoose.Schema( + { + ...(questSchema as any), + amount: Number, + platform: Number, + kind: String, + interaction: Number, + content: String, + contentMetadata: String, + }, + { timestamps: true }, + ), + 'questsocial', +); diff --git a/apps/api/src/app/models/QuestSocialEntry.ts b/apps/api/src/app/models/QuestSocialEntry.ts new file mode 100644 index 000000000..91776d4e5 --- /dev/null +++ b/apps/api/src/app/models/QuestSocialEntry.ts @@ -0,0 +1,34 @@ +import mongoose from 'mongoose'; + +export type QuestSocialEntryDocument = mongoose.Document & TQuestSocialEntry; + +export const QuestSocialEntry = mongoose.model<QuestSocialEntryDocument>( + 'QuestSocialEntry', + new mongoose.Schema( + { + questId: String, + sub: String, + amount: Number, + poolId: String, + metadata: { + platformUserId: String, + twitter: { + followersCount: Number, + followingCount: Number, + tweetCount: Number, + listedCount: Number, + likeCount: Number, + }, + discord: { + guildId: String, + username: String, + joinedAt: Date, + messageCount: Number, + reactionCount: Number, + }, + }, + }, + { timestamps: true }, + ), + 'questsocialentry', +); diff --git a/apps/api/src/app/models/QuestWeb3.ts b/apps/api/src/app/models/QuestWeb3.ts new file mode 100644 index 000000000..a74b95dd1 --- /dev/null +++ b/apps/api/src/app/models/QuestWeb3.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; +import { questSchema } from '@thxnetwork/api/models/Quest'; + +export type QuestWeb3Document = mongoose.Document & TQuestWeb3; + +export const QuestWeb3 = mongoose.model<QuestWeb3Document>( + 'QuestWeb3', + new mongoose.Schema( + { + ...(questSchema as any), + amount: Number, + contracts: Array({ + chainId: Number, + address: String, + }), + methodName: String, + threshold: String, + }, + { timestamps: true }, + ), + 'questweb3', +); diff --git a/apps/api/src/app/models/QuestWeb3Entry.ts b/apps/api/src/app/models/QuestWeb3Entry.ts new file mode 100644 index 000000000..ef5c44f87 --- /dev/null +++ b/apps/api/src/app/models/QuestWeb3Entry.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; + +export type QuestWeb3EntryDocument = mongoose.Document & TQuestWeb3Entry; + +export const QuestWeb3Entry = mongoose.model<QuestWeb3EntryDocument>( + 'QuestWeb3Entry', + new mongoose.Schema( + { + poolId: String, + questId: String, + sub: String, + amount: Number, + metadata: { + chainId: Number, + callResult: String, + address: String, + }, + }, + { timestamps: true }, + ), + 'questweb3entry', +); diff --git a/apps/api/src/app/models/QuestWebhook.ts b/apps/api/src/app/models/QuestWebhook.ts new file mode 100644 index 000000000..31ff80b15 --- /dev/null +++ b/apps/api/src/app/models/QuestWebhook.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; +import { questSchema } from './Quest'; + +export type QuestWebhookDocument = mongoose.Document & TQuestWebhook; + +export const QuestWebhook = mongoose.model<QuestWebhookDocument>( + 'QuestWebhook', + new mongoose.Schema( + { + ...(questSchema as any), + amount: Number, + webhookId: String, + metadata: String, + }, + { timestamps: true }, + ), + 'questwebhook', +); diff --git a/apps/api/src/app/models/QuestWebhookEntry.ts b/apps/api/src/app/models/QuestWebhookEntry.ts new file mode 100644 index 000000000..7968c5c68 --- /dev/null +++ b/apps/api/src/app/models/QuestWebhookEntry.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type QuestWebhookEntryDocument = mongoose.Document & TQuestWebhookEntry; + +export const QuestWebhookEntry = mongoose.model<QuestWebhookEntryDocument>( + 'QuestWebhookEntry', + new mongoose.Schema( + { + questId: String, + poolId: String, + webhookId: String, + identityId: String, + sub: String, + amount: Number, + }, + { timestamps: true }, + ), + 'questwebhookentry', +); diff --git a/apps/api/src/app/models/Reward.ts b/apps/api/src/app/models/Reward.ts new file mode 100644 index 000000000..7301a1d31 --- /dev/null +++ b/apps/api/src/app/models/Reward.ts @@ -0,0 +1,22 @@ +export const rewardSchema = { + uuid: String, + poolId: String, + title: String, + description: String, + image: String, + pointPrice: Number, + expiryDate: Date, + limit: Number, + isPromoted: { type: Boolean, default: false }, + isPublished: { type: Boolean, default: false }, + locks: { type: [{ questId: String, variant: Number }], default: [] }, + claimAmount: Number, + claimLimit: Number, +}; + +export const rewardPaymentSchema = { + poolId: String, + rewardId: String, + sub: String, + amount: Number, +}; diff --git a/apps/api/src/app/models/RewardCoin.ts b/apps/api/src/app/models/RewardCoin.ts new file mode 100644 index 000000000..a54d6108d --- /dev/null +++ b/apps/api/src/app/models/RewardCoin.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import { rewardSchema } from './Reward'; + +export type RewardCoinDocument = mongoose.Document & TRewardCoin; + +export const RewardCoin = mongoose.model<RewardCoinDocument>( + 'RewardCoin', + new mongoose.Schema( + { + ...rewardSchema, + variant: { type: Number, default: RewardVariant.Coin }, + erc20Id: String, + amount: String, + }, + { timestamps: true }, + ), + 'rewardcoin', +); diff --git a/apps/api/src/app/models/RewardCoinPayment.ts b/apps/api/src/app/models/RewardCoinPayment.ts new file mode 100644 index 000000000..5d9500190 --- /dev/null +++ b/apps/api/src/app/models/RewardCoinPayment.ts @@ -0,0 +1,17 @@ +import mongoose from 'mongoose'; + +export type RewardCoinPaymentDocument = mongoose.Document & TRewardCoinPayment; + +export const RewardCoinPayment = mongoose.model<RewardCoinPaymentDocument>( + 'RewardCoinPayment', + new mongoose.Schema( + { + rewardId: String, + sub: String, + poolId: String, + amount: Number, + }, + { timestamps: true }, + ), + 'rewardcoinpayment', +); diff --git a/apps/api/src/app/models/RewardCoupon.ts b/apps/api/src/app/models/RewardCoupon.ts new file mode 100644 index 000000000..badc6d39a --- /dev/null +++ b/apps/api/src/app/models/RewardCoupon.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; +import { rewardSchema } from './Reward'; +import { RewardVariant } from '@thxnetwork/common/enums'; + +export type RewardCouponDocument = mongoose.Document & TRewardCoupon; + +export const RewardCoupon = mongoose.model<RewardCouponDocument>( + 'RewardCoupon', + new mongoose.Schema( + { + ...rewardSchema, + variant: { type: Number, default: RewardVariant.Coupon }, + webshopURL: String, + }, + { timestamps: true }, + ), + 'rewardcoupon', +); diff --git a/apps/api/src/app/models/RewardCouponPayment.ts b/apps/api/src/app/models/RewardCouponPayment.ts new file mode 100644 index 000000000..025e9e52e --- /dev/null +++ b/apps/api/src/app/models/RewardCouponPayment.ts @@ -0,0 +1,16 @@ +import mongoose from 'mongoose'; +import { rewardPaymentSchema } from './Reward'; + +export type RewardCouponPaymentDocument = mongoose.Document & TRewardCouponPayment; + +export const RewardCouponPayment = mongoose.model<RewardCouponPaymentDocument>( + 'RewardCouponPayment', + new mongoose.Schema( + { + ...rewardPaymentSchema, + couponCodeId: String, + }, + { timestamps: true }, + ), + 'rewardcouponpayment', +); diff --git a/apps/api/src/app/models/RewardCustom.ts b/apps/api/src/app/models/RewardCustom.ts new file mode 100644 index 000000000..f7b8e42a9 --- /dev/null +++ b/apps/api/src/app/models/RewardCustom.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; +import { rewardSchema } from './Reward'; +import { RewardVariant } from '@thxnetwork/common/enums'; + +export type RewardCustomDocument = mongoose.Document & TRewardCustom; + +export const RewardCustom = mongoose.model<RewardCustomDocument>( + 'RewardCustom', + new mongoose.Schema( + { + ...rewardSchema, + variant: { type: Number, default: RewardVariant.Custom }, + metadata: String, + webhookId: String, + }, + { timestamps: true }, + ), + 'rewardcustom', +); diff --git a/apps/api/src/app/models/RewardCustomPayment.ts b/apps/api/src/app/models/RewardCustomPayment.ts new file mode 100644 index 000000000..459b79a16 --- /dev/null +++ b/apps/api/src/app/models/RewardCustomPayment.ts @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; +import { rewardPaymentSchema } from './Reward'; + +export type RewardCustomPaymentDocument = mongoose.Document & TRewardCustomPayment; + +export const RewardCustomPayment = mongoose.model<RewardCustomPaymentDocument>( + 'RewardCustomPayment', + new mongoose.Schema( + { + ...rewardPaymentSchema, + }, + { timestamps: true }, + ), + 'rewardcustompayment', +); diff --git a/apps/api/src/app/models/RewardDiscordRole.ts b/apps/api/src/app/models/RewardDiscordRole.ts new file mode 100644 index 000000000..f38ecd684 --- /dev/null +++ b/apps/api/src/app/models/RewardDiscordRole.ts @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; +import { rewardSchema } from './Reward'; +import { RewardVariant } from '@thxnetwork/common/enums'; + +export type RewardDiscordRoleDocument = mongoose.Document & TRewardDiscordRole; + +export const RewardDiscordRole = mongoose.model<RewardDiscordRoleDocument>( + 'RewardDiscordRole', + new mongoose.Schema( + { + ...rewardSchema, + variant: { type: Number, default: RewardVariant.DiscordRole }, + discordRoleId: String, + }, + { timestamps: true }, + ), + 'rewarddiscordrole', +); diff --git a/apps/api/src/app/models/RewardDiscordRolePayment.ts b/apps/api/src/app/models/RewardDiscordRolePayment.ts new file mode 100644 index 000000000..fccdf8ee8 --- /dev/null +++ b/apps/api/src/app/models/RewardDiscordRolePayment.ts @@ -0,0 +1,16 @@ +import mongoose from 'mongoose'; +import { rewardPaymentSchema } from './Reward'; + +export type RewardDiscordRolePaymentDocument = mongoose.Document & TRewardDiscordRolePayment; + +export const RewardDiscordRolePayment = mongoose.model<RewardDiscordRolePaymentDocument>( + 'RewardDiscordRolePayment', + new mongoose.Schema( + { + ...rewardPaymentSchema, + discordRoleId: String, + }, + { timestamps: true }, + ), + 'rewarddiscordrolepayment', +); diff --git a/apps/api/src/app/models/RewardGalachain.ts b/apps/api/src/app/models/RewardGalachain.ts new file mode 100644 index 000000000..75aec01c8 --- /dev/null +++ b/apps/api/src/app/models/RewardGalachain.ts @@ -0,0 +1,26 @@ +import mongoose from 'mongoose'; +import { rewardSchema } from './Reward'; +import { RewardVariant } from '@thxnetwork/common/enums'; + +export type RewardGalachainDocument = mongoose.Document & TRewardGalachain; + +export const RewardGalachain = mongoose.model<RewardGalachainDocument>( + 'RewardGalachain', + new mongoose.Schema( + { + ...rewardSchema, + variant: { type: Number, default: RewardVariant.Galachain }, + amount: String, + contractChannelName: { type: String, required: true }, + contractChaincodeName: { type: String, required: true }, + contractContractName: { type: String, required: true }, + tokenCollection: { type: String, required: true }, + tokenCategory: { type: String, required: true }, + tokenType: { type: String, required: true }, + tokenAdditionalKey: { type: String, required: true }, + tokenInstance: { type: Number, required: true }, + }, + { timestamps: true }, + ), + 'rewardgalachain', +); diff --git a/apps/api/src/app/models/RewardGalachainPayment.ts b/apps/api/src/app/models/RewardGalachainPayment.ts new file mode 100644 index 000000000..de4bf79e6 --- /dev/null +++ b/apps/api/src/app/models/RewardGalachainPayment.ts @@ -0,0 +1,16 @@ +import mongoose from 'mongoose'; +import { rewardPaymentSchema } from './Reward'; + +export type RewardGalachainPaymentDocument = mongoose.Document & TRewardGalachainPayment; + +export const RewardGalachainPayment = mongoose.model<RewardGalachainPaymentDocument>( + 'RewardGalachainPayment', + new mongoose.Schema( + { + ...rewardPaymentSchema, + amount: String, + }, + { timestamps: true }, + ), + 'rewardgalachainpayment', +); diff --git a/apps/api/src/app/models/RewardNFT.ts b/apps/api/src/app/models/RewardNFT.ts new file mode 100644 index 000000000..2ad9763ef --- /dev/null +++ b/apps/api/src/app/models/RewardNFT.ts @@ -0,0 +1,25 @@ +import mongoose from 'mongoose'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import { rewardSchema } from './Reward'; + +export type RewardNFTDocument = mongoose.Document & TRewardNFT; + +export const RewardNFT = mongoose.model<RewardNFTDocument>( + 'RewardNFT', + new mongoose.Schema( + { + ...rewardSchema, + variant: { type: Number, default: RewardVariant.NFT }, + erc721Id: String, + erc1155Id: String, + erc1155Amount: String, + metadataId: String, + tokenId: String, + price: Number, + priceCurrency: String, + redirectUrl: String, + }, + { timestamps: true }, + ), + 'rewardnft', +); diff --git a/apps/api/src/app/models/RewardNFTPayment.ts b/apps/api/src/app/models/RewardNFTPayment.ts new file mode 100644 index 000000000..d7d49b5fb --- /dev/null +++ b/apps/api/src/app/models/RewardNFTPayment.ts @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; +import { rewardPaymentSchema } from './Reward'; + +export type RewardNFTPaymentDocument = mongoose.Document & TRewardNFTPayment; + +export const RewardNFTPayment = mongoose.model<RewardNFTPaymentDocument>( + 'RewardNFTPayment', + new mongoose.Schema( + { + ...rewardPaymentSchema, + }, + { timestamps: true }, + ), + 'rewardnftpayment', +); diff --git a/apps/api/src/app/models/Transaction.ts b/apps/api/src/app/models/Transaction.ts new file mode 100644 index 000000000..53ccef3ad --- /dev/null +++ b/apps/api/src/app/models/Transaction.ts @@ -0,0 +1,30 @@ +import mongoose from 'mongoose'; + +export type TransactionDocument = mongoose.Document & TTransaction; + +export const Transaction = mongoose.model<TransactionDocument>( + 'Transaction', + new mongoose.Schema( + { + from: String, + to: String, + nonce: Number, + walletId: String, + transactionId: String, + transactionHash: String, + safeTxHash: String, + gas: String, + baseFee: String, + maxFeePerGas: String, + maxPriorityFeePerGas: String, + type: Number, + state: { type: Number, index: { sparse: true } }, + call: { fn: String, args: String }, + chainId: Number, + failReason: String, + callback: {}, + }, + { timestamps: true }, + ), + 'transaction', +); diff --git a/apps/api/src/app/models/TwitterFollower.ts b/apps/api/src/app/models/TwitterFollower.ts new file mode 100644 index 000000000..21a1e5b10 --- /dev/null +++ b/apps/api/src/app/models/TwitterFollower.ts @@ -0,0 +1,17 @@ +import mongoose from 'mongoose'; + +export type TwitterFollowerDocument = mongoose.Document & TTwitterFollower; + +const twitterFollowerSchema = new mongoose.Schema( + { + userId: String, + targetUserId: String, + }, + { timestamps: true }, +); + +export const TwitterFollower = mongoose.model<TwitterFollowerDocument>( + 'TwitterFollower', + twitterFollowerSchema, + 'twitterfollower', +); diff --git a/apps/api/src/app/models/TwitterLike.ts b/apps/api/src/app/models/TwitterLike.ts new file mode 100644 index 000000000..f759fef3d --- /dev/null +++ b/apps/api/src/app/models/TwitterLike.ts @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; + +export type TwitterLikeDocument = mongoose.Document & TTwitterLike; + +export const TwitterLike = mongoose.model<TwitterLikeDocument>( + 'TwitterLike', + new mongoose.Schema( + { + userId: String, + postId: String, + }, + { timestamps: true }, + ), + 'twitterlike', +); diff --git a/apps/api/src/app/models/TwitterPost.ts b/apps/api/src/app/models/TwitterPost.ts new file mode 100644 index 000000000..6f5c09e95 --- /dev/null +++ b/apps/api/src/app/models/TwitterPost.ts @@ -0,0 +1,25 @@ +import mongoose from 'mongoose'; + +export type TwitterPostDocument = mongoose.Document & TTwitterPost; + +export const TwitterPost = mongoose.model<TwitterPostDocument>( + 'TwitterPost', + new mongoose.Schema( + { + userId: String, + postId: String, + queryId: String, + text: String, + publicMetrics: { + retweetCount: Number, + replyCount: Number, + likeCount: Number, + quoteCount: Number, + bookmarkCount: Number, + impressionCount: Number, + }, + }, + { timestamps: true }, + ), + 'twitterpost', +); diff --git a/apps/api/src/app/models/TwitterQuery.ts b/apps/api/src/app/models/TwitterQuery.ts new file mode 100644 index 000000000..43ce2f1e9 --- /dev/null +++ b/apps/api/src/app/models/TwitterQuery.ts @@ -0,0 +1,33 @@ +import mongoose from 'mongoose'; + +export type TwitterQueryDocument = mongoose.Document & TTwitterQuery; + +export const TwitterQuery = mongoose.model<TwitterQueryDocument>( + 'TwitterQuery', + new mongoose.Schema( + { + poolId: String, + query: String, + operators: { + from: [String], + to: [String], + text: [String], + url: [String], + hashtags: [String], + mentions: [String], + media: String, + excludes: [String], + }, + defaults: { + title: String, + description: String, + amount: Number, + isPublished: Boolean, + expiryInDays: Number, + locks: [{ questId: String, variant: Number }], + }, + }, + { timestamps: true }, + ), + 'twitterquery', +); diff --git a/apps/api/src/app/models/TwitterRepost.ts b/apps/api/src/app/models/TwitterRepost.ts new file mode 100644 index 000000000..8d3b307c7 --- /dev/null +++ b/apps/api/src/app/models/TwitterRepost.ts @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; + +export type TwitterRepostDocument = mongoose.Document & TTwitterRepost; + +export const TwitterRepost = mongoose.model<TwitterRepostDocument>( + 'TwitterRepost', + new mongoose.Schema( + { + userId: String, + postId: String, + }, + { timestamps: true }, + ), + 'twitterrepost', +); diff --git a/apps/api/src/app/models/TwitterUser.ts b/apps/api/src/app/models/TwitterUser.ts new file mode 100644 index 000000000..c012f7b6b --- /dev/null +++ b/apps/api/src/app/models/TwitterUser.ts @@ -0,0 +1,24 @@ +import mongoose from 'mongoose'; + +export type TwitterUserDocument = mongoose.Document & TTwitterUser; + +export const TwitterUser = mongoose.model<TwitterUserDocument>( + 'TwitterUser', + new mongoose.Schema( + { + userId: String, + profileImgUrl: String, + name: String, + username: String, + publicMetrics: { + followersCount: Number, + followingCount: Number, + tweetCount: Number, + listedCount: Number, + likeCount: Number, + }, + }, + { timestamps: true }, + ), + 'twitteruser', +); diff --git a/apps/api/src/app/models/Wallet.ts b/apps/api/src/app/models/Wallet.ts new file mode 100644 index 000000000..6a0eb30e8 --- /dev/null +++ b/apps/api/src/app/models/Wallet.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; + +export type WalletDocument = mongoose.Document & TWallet; + +export const Wallet = mongoose.model<WalletDocument>( + 'Wallet', + new mongoose.Schema( + { + uuid: String, + expiresAt: Date, + poolId: String, + address: String, + sub: { type: String, index: 'hashed' }, + chainId: Number, + version: String, + safeVersion: String, + variant: String, + }, + { timestamps: true }, + ), + 'wallet', +); diff --git a/apps/api/src/app/models/Webhook.ts b/apps/api/src/app/models/Webhook.ts new file mode 100644 index 000000000..43841317d --- /dev/null +++ b/apps/api/src/app/models/Webhook.ts @@ -0,0 +1,17 @@ +import mongoose from 'mongoose'; + +export type WebhookDocument = mongoose.Document & TWebhook; + +export const Webhook = mongoose.model<WebhookDocument>( + 'Webhook', + new mongoose.Schema( + { + sub: String, + poolId: String, + url: String, + active: { default: false, type: Boolean }, + }, + { timestamps: true }, + ), + 'webhook', +); diff --git a/apps/api/src/app/models/WebhookRequest.ts b/apps/api/src/app/models/WebhookRequest.ts new file mode 100644 index 000000000..0d2d06228 --- /dev/null +++ b/apps/api/src/app/models/WebhookRequest.ts @@ -0,0 +1,19 @@ +import mongoose from 'mongoose'; + +export type WebhookRequestDocument = mongoose.Document & TWebhookRequest; + +export const WebhookRequest = mongoose.model<WebhookRequestDocument>( + 'WebhookRequest', + new mongoose.Schema( + { + webhookId: String, + payload: String, + attempts: { type: Number, default: 0 }, + state: Number, + httpStatus: Number, + failReason: String, + }, + { timestamps: true }, + ), + 'webhookrequest', +); diff --git a/apps/api/src/app/models/Widget.ts b/apps/api/src/app/models/Widget.ts new file mode 100644 index 000000000..49f6bd0ab --- /dev/null +++ b/apps/api/src/app/models/Widget.ts @@ -0,0 +1,23 @@ +import mongoose from 'mongoose'; + +export type WidgetDocument = mongoose.Document & TWidget; + +export const Widget = mongoose.model<WidgetDocument>( + 'Widget', + new mongoose.Schema( + { + uuid: String, + poolId: String, + iconImg: String, + align: String, + message: String, + domain: String, + theme: String, + cssSelector: String, + active: { default: false, type: Boolean }, + isPublished: { type: Boolean, default: true }, + }, + { timestamps: true }, + ), + 'widget', +); diff --git a/apps/api/src/app/models/index.ts b/apps/api/src/app/models/index.ts new file mode 100644 index 000000000..004dd492d --- /dev/null +++ b/apps/api/src/app/models/index.ts @@ -0,0 +1,61 @@ +export { Brand } from './Brand'; +export { QRCodeEntry, QRCodeEntryDocument } from './QRCodeEntry'; +export { Client, ClientDocument } from './Client'; +export { Collaborator, CollaboratorDocument } from './Collaborator'; +export { CouponCode, CouponCodeDocument } from './CouponCode'; +export { DiscordGuild, DiscordGuildDocument } from './DiscordGuild'; +export { DiscordMessage, DiscordMessageDocument } from './DiscordMessage'; +export { DiscordReaction, DiscordReactionDocument } from './DiscordReaction'; +export { ERC1155, ERC1155Document } from './ERC1155'; +export { ERC1155Metadata, ERC1155MetadataDocument } from './ERC1155Metadata'; +export { ERC1155Token, ERC1155TokenDocument } from './ERC1155Token'; +export { ERC20, ERC20Document } from './ERC20'; +export { ERC20Token, ERC20TokenDocument } from './ERC20Token'; +export { ERC20Transfer, ERC20TransferDocument } from './ERC20Transfer'; +export { ERC721, ERC721Document } from './ERC721'; +export { ERC721Metadata, ERC721MetadataDocument } from './ERC721Metadata'; +export { ERC721Token, ERC721TokenDocument } from './ERC721Token'; +export { ERC721Transfer, ERC721TransferDocument } from './ERC721Transfer'; +export { Event, EventDocument } from './Event'; +export { Identity, IdentityDocument } from './Identity'; +export { Invoice, InvoiceDocument } from './Invoice'; +export { Job, JobDocument } from './Job'; +export { Notification, NotificationDocument } from './Notification'; +export { Participant, ParticipantDocument } from './Participant'; +export { Pool, PoolDocument } from './Pool'; +export { QuestCustom, QuestCustomDocument } from './QuestCustom'; +export { QuestCustomEntry, QuestCustomEntryDocument } from './QuestCustomEntry'; +export { QuestDaily, QuestDailyDocument } from './QuestDaily'; +export { QuestDailyEntry, QuestDailyEntryDocument } from './QuestDailyEntry'; +export { QuestWebhook, QuestWebhookDocument } from './QuestWebhook'; +export { QuestWebhookEntry, QuestWebhookEntryDocument } from './QuestWebhookEntry'; +export { QuestGitcoin, QuestGitcoinDocument } from './QuestGitcoin'; +export { QuestGitcoinEntry, QuestGitcoinEntryDocument } from './QuestGitcoinEntry'; +export { QuestInvite, QuestInviteDocument } from './QuestInvite'; +export { QuestInviteEntry, QuestInviteEntryDocument } from './QuestInviteEntry'; +export { QuestSocial, QuestSocialDocument } from './QuestSocial'; +export { QuestSocialEntry, QuestSocialEntryDocument } from './QuestSocialEntry'; +export { QuestWeb3, QuestWeb3Document } from './QuestWeb3'; +export { QuestWeb3Entry, QuestWeb3EntryDocument } from './QuestWeb3Entry'; +export { RewardCoin, RewardCoinDocument } from './RewardCoin'; +export { RewardCoinPayment, RewardCoinPaymentDocument } from './RewardCoinPayment'; +export { RewardCoupon, RewardCouponDocument } from './RewardCoupon'; +export { RewardCouponPayment, RewardCouponPaymentDocument } from './RewardCouponPayment'; +export { RewardCustom, RewardCustomDocument } from './RewardCustom'; +export { RewardCustomPayment, RewardCustomPaymentDocument } from './RewardCustomPayment'; +export { RewardDiscordRole, RewardDiscordRoleDocument } from './RewardDiscordRole'; +export { RewardDiscordRolePayment, RewardDiscordRolePaymentDocument } from './RewardDiscordRolePayment'; +export { RewardNFT, RewardNFTDocument } from './RewardNFT'; +export { RewardNFTPayment, RewardNFTPaymentDocument } from './RewardNFTPayment'; +export { RewardGalachain, RewardGalachainDocument } from './RewardGalachain'; +export { RewardGalachainPayment, RewardGalachainPaymentDocument } from './RewardGalachainPayment'; +export { Transaction, TransactionDocument } from './Transaction'; +export { TwitterFollower, TwitterFollowerDocument } from './TwitterFollower'; +export { TwitterLike, TwitterLikeDocument } from './TwitterLike'; +export { TwitterRepost, TwitterRepostDocument } from './TwitterRepost'; +export { TwitterUser, TwitterUserDocument } from './TwitterUser'; +export { TwitterQuery, TwitterQueryDocument } from './TwitterQuery'; +export { Wallet, WalletDocument } from './Wallet'; +export { Webhook, WebhookDocument } from './Webhook'; +export { WebhookRequest, WebhookRequestDocument } from './WebhookRequest'; +export { Widget, WidgetDocument } from './Widget'; diff --git a/apps/api/src/app/proxies/AccountProxy.ts b/apps/api/src/app/proxies/AccountProxy.ts new file mode 100644 index 000000000..097c2f33b --- /dev/null +++ b/apps/api/src/app/proxies/AccountProxy.ts @@ -0,0 +1,113 @@ +import { authClient, getAuthAccessToken } from '@thxnetwork/api/util/auth'; +import { BadRequestError } from '../util/errors'; +import { AccessTokenKind, OAuthScope } from '@thxnetwork/common/enums'; +import { AxiosRequestConfig } from 'axios'; + +export default class AccountProxy { + static async request(config: AxiosRequestConfig) { + const { status, data } = await authClient({ + ...config, + headers: { + Authorization: await getAuthAccessToken(), + }, + }); + + if (status >= 400 && status <= 500 && data.error) { + throw new BadRequestError(data.error.message); + } + + return data; + } + + static async getToken(account: TAccount, kind: AccessTokenKind, requiredScopes: OAuthScope[] = []) { + const token = await this.request({ + method: 'GET', + url: `/accounts/${account.sub}/tokens/${kind}`, + }); + if (token && requiredScopes.every((scope) => token.scopes.includes(scope))) return token; + } + + static disconnect(account: TAccount, kind: AccessTokenKind) { + return this.request({ + method: 'DELETE', + url: `/accounts/${account.sub}/tokens/${kind}`, + }); + } + + static findById(sub: string): Promise<TAccount> { + return this.request({ + method: 'GET', + url: `/accounts/${sub}`, + }); + } + + static update(sub: string, updates: Partial<TAccount>): Promise<TAccount> { + return this.request({ + method: 'PATCH', + url: `/accounts/${sub}`, + data: updates, + }); + } + + static remove(sub: string) { + return this.request({ + method: 'DELETE', + url: `/accounts/${sub}`, + }); + } + + static find({ subs, query }: Partial<{ subs: string[]; query: string }>): Promise<TAccount[]> { + return this.request({ + method: 'POST', + url: '/accounts', + data: { subs: JSON.stringify(subs), query }, + }); + } + + static getByDiscordId(discordId: string): Promise<TAccount> { + return this.request({ + method: 'GET', + url: `/accounts/discord/${discordId}`, + }); + } + + static getByEmail(email: string): Promise<TAccount> { + return this.request({ + method: 'GET', + url: `/accounts/email/${email}`, + }); + } + + static getByAddress(address: string): Promise<TAccount> { + return this.request({ + method: 'GET', + url: `/accounts/address/${address}`, + }); + } + + static getByIdentity(identity: string): Promise<TAccount> { + return this.request({ + method: 'GET', + url: `/accounts/identity/${identity}`, + }); + } + + static async isEmailDuplicate(email: string) { + try { + await authClient({ + method: 'GET', + url: `/accounts/email/${email}`, + headers: { + Authorization: await getAuthAccessToken(), + }, + }); + + return true; + } catch (error) { + if (error.response.status === 404) { + return false; + } + throw error; + } + } +} diff --git a/apps/api/src/app/proxies/ClientProxy.ts b/apps/api/src/app/proxies/ClientProxy.ts new file mode 100644 index 000000000..812a5d3f0 --- /dev/null +++ b/apps/api/src/app/proxies/ClientProxy.ts @@ -0,0 +1,87 @@ +import { INITIAL_ACCESS_TOKEN } from '@thxnetwork/api/config/secrets'; +import { Client, ClientDocument, TClient, TClientPayload } from '@thxnetwork/api/models/Client'; +import { authClient } from '@thxnetwork/api/util/auth'; +import { paginatedResults } from '@thxnetwork/api/util/pagination'; + +export default class ClientProxy { + static async getCredentials(client: ClientDocument) { + const { data } = await authClient({ + method: 'GET', + url: `/reg/${client.clientId}?access_token=${client.registrationAccessToken}`, + }); + + client.clientSecret = data['client_secret']; + client.requestUris = data['request_uris']; + + return client; + } + + static async get(id: string): Promise<TClient> { + const client = await Client.findById(id); + return await this.getCredentials(client); + } + + static async findByClientId(clientId: string): Promise<TClient> { + const client = await Client.findOne({ clientId }); + return await this.getCredentials(client); + } + + static async isAllowedOrigin(origin: string) { + return Client.exists({ origins: origin }); + } + + static async findByQuery(query: { poolId: string }, page = 1, limit = 10) { + return paginatedResults(Client, page, limit, query); + } + + static async create(sub: string, poolId: string, payload: TClientPayload, name?: string) { + const { data } = await authClient({ + method: 'POST', + url: '/reg', + headers: { + Authorization: `Bearer ${INITIAL_ACCESS_TOKEN}`, + }, + data: payload, + }); + + const client = await Client.create({ + sub, + name, + poolId, + grantType: payload.grant_types[0], + clientId: data.client_id, + registrationAccessToken: data.registration_access_token, + }); + + if (payload.request_uris && payload.request_uris.length) { + const origins = payload.request_uris.map((uri: string) => new URL(uri)); + await client.updateOne({ origins }); + } + + client.clientSecret = data['client_secret']; + client.requestUris = data['request_uris']; + + return client; + } + + static async remove(clientId: string) { + const client = await Client.findOne({ clientId }); + + await authClient({ + method: 'DELETE', + url: `/reg/${client.clientId}?access_token=${client.registrationAccessToken}`, + }); + + await client.deleteOne(); + } + + static async update(clientId: string, updates: TClientUpdatePayload) { + const client = await Client.findOne({ clientId }); + await client.updateOne(updates); + return this.getCredentials(client); + } +} + +export type TClientUpdatePayload = Partial<{ + name: string; +}>; diff --git a/apps/api/src/app/proxies/DiscordDataProxy.ts b/apps/api/src/app/proxies/DiscordDataProxy.ts new file mode 100644 index 000000000..f5a074ffd --- /dev/null +++ b/apps/api/src/app/proxies/DiscordDataProxy.ts @@ -0,0 +1,156 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { client, PermissionFlagsBits } from '../../discord'; +import { DiscordGuild, DiscordGuildDocument, PoolDocument } from '@thxnetwork/api/models'; +import { ActionRowBuilder, ButtonBuilder, Guild } from 'discord.js'; +import { WIDGET_URL } from '../config/secrets'; +import { logger } from '../util/logger'; +import { AccessTokenKind, OAuthRequiredScopes } from '@thxnetwork/common/enums'; +import { DISCORD_API_ENDPOINT } from '@thxnetwork/common/constants'; +import AccountProxy from './AccountProxy'; +import { discordColorToHex } from '../util/discord'; + +export enum NotificationVariant { + QuestDaily = 0, + QuestInvite = 1, + QuestYouTube = 3, + QuestTwitter = 4, + QuestDiscord = 5, + QuestCustom = 6, + QuestWeb3 = 7, +} + +export async function discordClient(config: AxiosRequestConfig) { + try { + const client = axios.create({ ...config, baseURL: DISCORD_API_ENDPOINT }); + return await client(config); + } catch (error) { + console.error(error); + } +} + +export default class DiscordDataProxy { + static async sendChannelMessage( + pool: PoolDocument, + content: string, + embeds: TDiscordEmbed[] = [], + buttons?: TDiscordButton[], + ) { + try { + const discordGuild = await DiscordGuild.findOne({ poolId: String(pool._id) }); + const url = WIDGET_URL + `/c/${pool.settings.slug}/quests`; + + if (discordGuild && discordGuild.channelId) { + const channel: any = await client.channels.fetch(discordGuild.channelId); + const components = []; + if (buttons) components.push(this.createButtonActionRow(buttons)); + + const botMember = channel.guild.members.cache.get(client.user.id); + if (!botMember.permissionsIn(channel).has(PermissionFlagsBits.SendMessages)) { + throw new Error('Insufficient channel permissions for bot to send messages.'); + } + + channel.send({ content, embeds, components }); + } else if (pool.settings.discordWebhookUrl) { + // Extending the content with a link as we're not allowed to send button components over webhooks + content += ` [Complete Quest ▸](<${url}>)`; + axios.post(pool.settings.discordWebhookUrl, { content, embeds }); + } + } catch (error) { + logger.error(error); + } + } + + static createButtonActionRow(buttons: TDiscordButton[]) { + const components = buttons.map((btn: TDiscordButton) => { + const button = new ButtonBuilder().setLabel(btn.label).setStyle(btn.style); + if (btn.customId) button.setCustomId(btn.customId); + if (btn.url) button.setURL(btn.url); + if (btn.emoji) button.setEmoji(btn.emoji); + if (btn.disabled) button.setDisabled(true); + return button; + }); + return new ActionRowBuilder().addComponents(components); + } + + static async getGuilds(token: TToken) { + const r = await discordClient({ + method: 'GET', + url: '/users/@me/guilds', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token.accessToken}`, + }, + }); + return r.data; + } + + static async validateGuildJoined(account: TAccount, guildId: string) { + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Discord, + OAuthRequiredScopes.DiscordValidateGuild, + ); + if (!token) return { result: false, reason: 'Could not find a Discord access_token for this account.' }; + + const guilds = await this.getGuilds(token); + const isUserJoinedGuild = guilds.find((guild) => guild.id === guildId); + if (isUserJoinedGuild) return { result: true, reason: '' }; + + return { result: false, reason: 'Discord: Your Discord account is not a member of this server.' }; + } + + static async validateGuildRole(account: TAccount, guildId: string, roleId: string) { + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Discord, + OAuthRequiredScopes.DiscordValidateGuild, + ); + if (!token) return { result: false, reason: 'Could not find a Discord access_token for this account.' }; + + // Fetch guild from bot + const guild = await this.fetchGuild(guildId); + if (!guild) return { result: false, reason: 'THX Bot is not in the server.' }; + + // Check role for guild member + const member = await guild.members.fetch(token.userId); + console.log(member.roles.cache.has(roleId), roleId); + if (member.roles.cache.has(roleId)) return { result: true, reason: '' }; + + return { result: false, reason: 'You do not have the required role.' }; + } + + static async getGuildRoles(guild: Guild) { + return guild.roles.cache.map((role) => ({ + id: role.id, + name: role.name, + color: discordColorToHex(role.color), + })); + } + + static async getGuildChannels(guild: Guild) { + const channels = await guild.channels.fetch(); + return channels.map((c) => ({ name: c.name, channelId: c.id })); + } + + static async fetchGuild(guildId: string) { + try { + return await client.guilds.fetch(guildId); + } catch (error) { + return; + } + } + + static async getGuild(guild: DiscordGuildDocument) { + const botGuild = await this.fetchGuild(guild.guildId); + const roles = botGuild ? await this.getGuildRoles(botGuild) : []; + const channels = botGuild ? await this.getGuildChannels(botGuild) : []; + + return { + ...guild, + roles, + channels, + isInvited: !!botGuild, + }; + } +} diff --git a/apps/api/src/app/proxies/TwitterDataProxy.ts b/apps/api/src/app/proxies/TwitterDataProxy.ts new file mode 100644 index 000000000..8b2780adc --- /dev/null +++ b/apps/api/src/app/proxies/TwitterDataProxy.ts @@ -0,0 +1,442 @@ +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { AccessTokenKind, OAuthRequiredScopes, OAuthTwitterScope } from '@thxnetwork/common/enums'; +import { TWITTER_API_ENDPOINT } from '@thxnetwork/common/constants'; +import { formatDistance } from 'date-fns'; +import { logger } from '../util/logger'; +import { TwitterLike } from '../models/TwitterLike'; +import { TwitterRepost } from '../models/TwitterRepost'; +import { TwitterUser } from '../models/TwitterUser'; +import { TwitterFollower } from '../models/TwitterFollower'; +import TwitterCacheService from '../services/TwitterCacheService'; +import AccountProxy from './AccountProxy'; + +async function twitterClient(config: AxiosRequestConfig) { + const client = axios.create({ ...config, baseURL: TWITTER_API_ENDPOINT }); + return await client(config); +} + +export default class TwitterDataProxy { + static async getUserByUsername(account: TAccount, username: string) { + const token = await AccountProxy.getToken(account, AccessTokenKind.Twitter, [ + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + ]); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + + const data = await this.request(token, { + method: 'GET', + url: `/users/by/username/${username}`, + params: { + 'user.fields': 'profile_image_url,public_metrics', + }, + }); + + return data.data; + } + + static async getUser(account: TAccount, userId: string) { + const token = await AccountProxy.getToken(account, AccessTokenKind.Twitter, [ + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + ]); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + + try { + const data = await this.request(token, { + method: 'GET', + url: `/users/${userId}`, + params: { + 'user.fields': 'profile_image_url,public_metrics', + }, + }); + + // Cache TwitterUser + await TwitterUser.findOneAndUpdate( + { userId: data.data.id }, + { + userId: data.data.id, + profileImgUrl: data.data.profile_image_url, + name: data.data.name, + username: data.data.username, + publicMetrics: { + followersCount: data.data.public_metrics.followers_count, + followingCount: data.data.public_metrics.following_count, + tweetCount: data.data.public_metrics.tweet_count, + listedCount: data.data.public_metrics.listed_count, + likeCount: data.data.public_metrics.like_count, + }, + }, + { upsert: true }, + ); + + return data.data; + } catch (res) { + return this.handleError(account, token, res); + } + } + + static async getTweet(account: TAccount, tweetId: string) { + const token = await AccountProxy.getToken(account, AccessTokenKind.Twitter, [ + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + ]); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + + const data = await this.request(token, { + method: 'GET', + url: `/tweets`, + params: { + ids: tweetId, + expansions: 'author_id', + }, + }); + + return { tweet: data.data[0], user: data.includes.users[0] }; + } + + /** + * '/tweets/search/recent' + * + * Rate Limits: + * Application-only: 450 requests per 15-minute window shared among all users of your app + * User context: 180 requests per 15-minute window per each authenticated user + */ + static async search( + account: TAccount, + query: string, + options: { + 'max_results': number; + 'media.fields': string; + 'tweet.fields': string; + 'user.fields': string; + 'expansions': string; + 'sort_order': string; + } = { + 'max_results': 10, + 'sort_order': 'recency', // relevancy / recency + 'expansions': 'author_id,attachments.media_keys', + 'tweet.fields': 'public_metrics,possibly_sensitive,created_at,attachments', + 'user.fields': 'public_metrics,username,profile_image_url,verified,verified_type', + 'media.fields': 'public_metrics,url,preview_image_url,width,height,alt_text', + }, + ) { + const token = await AccountProxy.getToken(account, AccessTokenKind.Twitter, [ + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + ]); + try { + logger.info(`Twitter Query: "${query}"`); + + const data = await this.request(token, { + url: '/tweets/search/recent', + method: 'GET', + params: { query, ...options }, + }); + logger.info(`Twitter Query Results: ${data.meta.result_count}`); + if (!data.meta.result_count) return []; + + return data.data.map((post) => { + const user = data.includes.users.find((user) => user.id === post.author_id); + const media = + post.attachments && + post.attachments.media_keys && + post.attachments.media_keys.map((key) => data.includes.media.find((m) => m.media_key === key)); + return { + user, + media, + ...post, + }; + }); + } catch (error) { + logger.error(error); + return []; + } + } + + static async searchTweets(account: TAccount, query: string) { + const token = await AccountProxy.getToken(account, AccessTokenKind.Twitter, [ + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + ]); + const startTime = new Date(Date.now() - 60 * 60 * 24).toISOString(); // 24h ago + const data = await this.request(token, { + url: '/tweets/search/recent', + method: 'GET', + params: { + query: `from:${token.userId} ${query}`, + start_time: startTime, + }, + }); + return data.data; + } + + static async validateUser(account: TAccount, quest: TQuestSocial) { + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Twitter, + OAuthRequiredScopes.TwitterValidateUser, + ); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + + const metadata = JSON.parse(quest.contentMetadata); + const minFollowersCount = metadata.minFollowersCount ? Number(metadata.minFollowersCount) : 0; + + try { + const user = await this.getUser(account, token.userId); + + // Validate the follower count for this user + const followersCount = user.public_metrics.followers_count; + if (followersCount >= minFollowersCount) return { result: true, reason: '' }; + + return { + result: false, + reason: `X: Your account does not meet the threshold of ${minFollowersCount} followers.`, + }; + } catch (res) { + return this.handleError(account, token, res); + } + } + + static async validateFollow(account: TAccount, userId: string) { + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Twitter, + OAuthRequiredScopes.TwitterValidateFollow, + ); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + try { + if (token.userId === userId) { + return { result: false, reason: 'X: Can not validate a follow for your account with your account.' }; + } + + const data = await this.request(token, { + url: `/users/${token.userId}/following`, + method: 'POST', + data: { + target_user_id: userId, + }, + }); + + // Cache TwitterFollower here if isFollowing is true + await TwitterFollower.findOneAndUpdate( + { userId: token.userId, targetUserId: userId }, + { userId: token.userId, targetUserId: userId }, + { upsert: true }, + ); + + if (data.data.following) { + return { result: true, reason: '' }; + } + + return { result: false, reason: 'X: Account is not found as a follower.' }; + } catch (res) { + return this.handleError(account, token, res); + } + } + + static async validateLike(account: TAccount, quest: TQuestSocial) { + const postId = quest.content; + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Twitter, + OAuthRequiredScopes.TwitterValidateLike, + ); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + + // Search for TwitterLikes with this userId and postId in the cache + const like = await TwitterLike.findOne({ userId: token.userId, postId }); + if (like) { + logger.info( + `[${quest.poolId}][${account.sub}] X Quest ${quest._id} Like verification resolves from cache.`, + ); + return { result: true, reason: '' }; + } + + try { + // No cache result means we should update the cache. + await TwitterCacheService.updateLikeCache(account, quest, token); + + // Search the database again after a complete cache update that is not rate limited + const like = await this.findLike(token.userId, postId); + if (like) return { result: true, reason: '' }; + + // Fail if nothing is found + return { result: false, reason: 'X: Post has not been not liked.' }; + } catch (res) { + // Search the database again after a partial cache update that threw an error + const like = await this.findLike(token.userId, postId); + if (like) return { result: true, reason: '' }; + + // If not found amongst the latest cache update then we show the rate limit error + return this.handleError(account, token, res); + } + } + + static async validateRetweet(account: TAccount, quest: TQuestSocial) { + const postId = quest.content; + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Twitter, + OAuthRequiredScopes.TwitterValidateRepost, + ); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + + // Query cached TwitterReposts for this tweetId and userId + const repost = await TwitterRepost.findOne({ userId: token.userId, postId }); + if (repost) { + logger.info( + `[${quest.poolId}][${account.sub}] X Quest ${quest._id} Repost verification resolves from cache.`, + ); + return { result: true, reason: '' }; + } + + try { + // No cache result means we should update the cache. + await TwitterCacheService.updateRepostCache(account, quest, token); + + // Search the database again after a complete cache update that is not rate limited + const repost = await this.findRepost(token.userId, postId); + if (repost) return { result: true, reason: '' }; + + // Fail if nothing is found + return { result: false, reason: 'X: Post has not been not reposted.' }; + } catch (res) { + // Search the database again after a partial cache update that threw an error + const repost = await this.findRepost(token.userId, postId); + if (repost) return { result: true, reason: '' }; + + return this.handleError(account, token, res); + } + } + + static async validateQuery(account: TAccount, quest: TQuestSocial) { + const token = await AccountProxy.getToken( + account, + AccessTokenKind.Twitter, + OAuthRequiredScopes.TwitterValidateMessage, + ); + if (!token) return { result: false, reason: 'X: Could not find a connection for this account.' }; + if (!token.metadata || !token.metadata.username) { + return { result: false, reason: 'X: Could not find your username. Please reconnect your X account.' }; + } + + // Check connected X account username is known + const { operators } = JSON.parse(quest.contentMetadata); + if (!token.metadata || !token.metadata.username) { + return { result: false, reason: 'X: Could not find your username. Please reconnect your X account.' }; + } + + // Check if account username is among the required post authors if this operator is available + const authorWhitelist = operators.from.map((author) => author); + const username = token.metadata.username.toLowerCase(); + if (operators.from.length && !authorWhitelist.includes(username)) { + return { + result: false, + reason: `X: Your X account @${token.metadata.username} is not whitelisted for this quest.`, + }; + } + + try { + // If there is an author requirement we do not add the from operator for the current user + // and search for the given list instead + const query = operators.from.length ? quest.content : `from:${username} ${quest.content}`; + + // Not checking the cache here on purpose as we would need to reverse engineer + // the query logic in order to find matched similar to how X would + const posts = await this.search(account, query); + if (!posts.length) { + return { + result: false, + reason: `X: Could not find a post matching the requirements in the last 7 days.`, + }; + } + + // Cache the posts for future reference and display in UI + await TwitterCacheService.savePosts(posts); + + // Check if account username is among the results + const authorUsernames = posts.map((result) => result.user.username.toLowerCase()); + if (!authorUsernames.includes(token.metadata.username.toLowerCase())) { + return { + result: false, + reason: `X: Your X account @${token.metadata.username} is not found among the matched posts.`, + }; + } + + return { result: true, reason: '' }; + } catch (res) { + return this.handleError(account, token, res); + } + } + + static findLike(userId: string, postId: string) { + return TwitterLike.findOne({ userId, postId }); + } + + static findRepost(userId: string, postId: string) { + return TwitterRepost.findOne({ userId, postId }); + } + + static async request(token: TToken, config: AxiosRequestConfig) { + try { + const { data } = await twitterClient({ + ...config, + headers: { Authorization: `Bearer ${token.accessToken}` }, + }); + return data; + } catch (error) { + if (error.response) { + // Rethrow if this is an axios error + throw error.response; + } else { + logger.error(error); + } + } + } + + static async handleError(account: TAccount, token: TToken, res: AxiosResponse) { + if (res.status === 429) { + logger.info(`[429] X-RateLimit is hit by account ${account.sub} with X UserId ${token.userId}.`); + return this.handleRateLimitError(res); + } + + if (res.status === 401) { + logger.info(`[401] Token for ${account.sub} with X UserId ${token.userId} is invalid and disconnected.`); + await AccountProxy.disconnect(account, token.kind); + return { result: false, reason: 'Your X account connection has been removed, please reconnect!' }; + } + + if (res.status === 403) { + logger.info(`[403] Token for ${account.sub} with X UserId ${token.userId} has insufficient permissions.`); + return { result: false, reason: 'Your X account access level is insufficient, please reconnect!' }; + } + + logger.error(res); + + return { result: false, reason: 'X: An unexpected issue occured during your request.' }; + } + + private static handleRateLimitError(res: AxiosResponse) { + const limit = res.headers['x-rate-limit-limit']; + const resetTime = Number(res.headers['x-rate-limit-reset']); + const seconds = resetTime - Math.ceil(Date.now() / 1000); + + return { + result: false, + reason: `Quest requirement not found yet! We can only check ${ + limit * 100 + } items every 15 minutes. Please wait ${formatDistance(0, seconds * 1000, { + includeSeconds: true, + })} before retrying. Thank you!`, + }; + } + + private static parseSearchQuery(content: string) { + const emojiRegex = /<a?:.+?:\d{18}>|\p{Extended_Pictographic}/gu; + return content + .split(emojiRegex) + .filter((text) => text && text.length > 1 && !text.match(emojiRegex)) + .map((text) => `"${text}"`) + .join(' '); + } +} diff --git a/apps/api/src/app/proxies/YoutubeDataProxy.ts b/apps/api/src/app/proxies/YoutubeDataProxy.ts new file mode 100644 index 000000000..482c7e7c8 --- /dev/null +++ b/apps/api/src/app/proxies/YoutubeDataProxy.ts @@ -0,0 +1,53 @@ +import { google } from 'googleapis'; +import { AccessTokenKind, OAuthRequiredScopes, OAuthScope } from '@thxnetwork/common/enums'; +import { AUTH_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '../config/secrets'; +import AccountProxy from './AccountProxy'; + +const client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, AUTH_URL + '/oidc/callback/google'); + +google.options({ auth: client }); + +async function getClient(account: TAccount, requiredScopes: OAuthScope[]) { + const token = await AccountProxy.getToken(account, AccessTokenKind.Google, requiredScopes); + client.setCredentials({ + access_token: token.accessToken, + refresh_token: token.refreshToken, + }); + return google.youtube({ version: 'v3' }); +} + +export default class YoutubeDataProxy { + static async validateLike(account: TAccount, videoId: string, nextPageToken?: string) { + const youtube = await getClient(account, OAuthRequiredScopes.GoogleYoutubeLike); + const { data } = await youtube.videos.list({ + part: ['snippet'], + myRating: 'like', + maxResults: 50, + pageToken: nextPageToken, + }); + + const isLiked = data.items.find((item) => item.id === videoId); + if (isLiked) return { result: true, reason: '' }; + + // NOTE Disabled paging as we hit rate limits when searching + // through all liked videos of a user. + // if (data.nextPageToken) { + // return await this.validateLike(account, content, nextPageToken); + // } + + return { result: false, reason: 'YouTube: Could not find your like for this video.' }; + } + + static async validateSubscribe(account: TAccount, channelId: string) { + const youtube = await getClient(account, OAuthRequiredScopes.GoogleYoutubeLike); + const { data } = await youtube.subscriptions.list({ + forChannelId: channelId, + part: ['snippet'], + mine: true, + }); + const isSubscribed = data.items.length > 0; + if (isSubscribed) return { result: true, reason: '' }; + + return { result: false, reason: 'Could not find your subscription for this channel.' }; + } +} diff --git a/apps/api/src/app/services/AnalyticsService.ts b/apps/api/src/app/services/AnalyticsService.ts new file mode 100644 index 000000000..297f57ce8 --- /dev/null +++ b/apps/api/src/app/services/AnalyticsService.ts @@ -0,0 +1,552 @@ +import mongoose from 'mongoose'; +import { RewardCoinDocument, RewardCoin } from '@thxnetwork/api/models/RewardCoin'; +import { RewardNFTDocument, RewardNFT } from '@thxnetwork/api/models/RewardNFT'; +import { QuestInviteDocument, QuestInvite } from '@thxnetwork/api/models/QuestInvite'; +import { + PoolDocument, + RewardCustomDocument, + QuestCustomDocument, + QuestSocialDocument, + QuestWeb3Document, + QuestWeb3, + QuestSocialEntry, + QuestInviteEntry, + QuestWeb3Entry, + RewardCoinPayment, + RewardNFTPayment, + WalletDocument, + Participant, + RewardCouponDocument, + RewardCustom, + RewardDiscordRoleDocument, + RewardCouponPayment, + RewardCustomPayment, + RewardDiscordRolePayment, + RewardCoupon, + RewardDiscordRole, + QuestCustomEntry, + QuestDailyEntry, + QuestCustom, + QuestSocial, + QuestDailyDocument, + QuestDaily, + QuestGitcoin, + QuestGitcoinEntry, + Wallet, + RewardGalachainDocument, + RewardGalachain, + QuestGitcoinDocument, + RewardGalachainPayment, +} from '@thxnetwork/api/models'; + +async function getPoolAnalyticsForChart(pool: PoolDocument, startDate: Date, endDate: Date) { + // Rewards + const [ + erc20PerksQueryResult, + erc721PerksQueryResult, + customRewardsQueryResult, + couponRewardsQueryResult, + discordRoleRewardsQueryResult, + galachainRewardsQueryResult, + ] = await Promise.all([ + queryRewardRedemptions<RewardCoinDocument>({ + collectionName: 'rewardcoinpayment', + key: 'rewardId', + model: RewardCoin, + poolId: String(pool._id), + startDate, + endDate, + }), + queryRewardRedemptions<RewardNFTDocument>({ + collectionName: 'rewardnftpayment', + key: 'rewardId', + model: RewardNFT, + poolId: String(pool._id), + startDate, + endDate, + }), + queryRewardRedemptions<RewardCustomDocument>({ + collectionName: 'rewardcustompayment', + key: 'rewardId', + model: RewardCustom, + poolId: String(pool._id), + startDate, + endDate, + }), + queryRewardRedemptions<RewardCouponDocument>({ + collectionName: 'rewardcouponpayment', + key: 'rewardId', + model: RewardCoupon, + poolId: String(pool._id), + startDate, + endDate, + }), + queryRewardRedemptions<RewardDiscordRoleDocument>({ + collectionName: 'rewarddiscordrolepayment', + key: 'rewardId', + model: RewardDiscordRole, + poolId: String(pool._id), + startDate, + endDate, + }), + queryRewardRedemptions<RewardGalachainDocument>({ + collectionName: 'rewargalachainpayment', + key: 'rewardId', + model: RewardGalachain, + poolId: String(pool._id), + startDate, + endDate, + }), + ]); + + // Quests + const [ + milestoneRewardsQueryResult, + referralRewardsQueryResult, + pointRewardsQueryResult, + dailyRewardsQueryResult, + web3QuestsQueryResult, + gitcoinQuestsQueryResult, + ] = await Promise.all([ + queryQuestEntries<QuestCustomDocument>({ + collectionName: 'questcustomentry', + key: 'questId', + model: QuestCustom, + poolId: String(pool._id), + startDate, + endDate, + extraFilter: { isClaimed: true }, + }), + queryQuestEntries<QuestInviteDocument>({ + collectionName: 'questinviteentry', + key: 'questId', + model: QuestInvite, + poolId: String(pool._id), + startDate, + endDate, + extraFilter: { isApproved: true }, + }), + queryQuestEntries<QuestSocialDocument>({ + collectionName: 'questsocialentry', + key: 'questId', + model: QuestSocial, + poolId: String(pool._id), + startDate, + endDate, + }), + queryQuestEntries<QuestDailyDocument>({ + collectionName: 'questdailyentry', + key: 'questId', + model: QuestDaily, + poolId: String(pool._id), + startDate, + endDate, + }), + queryQuestEntries<QuestWeb3Document>({ + collectionName: 'questweb3entry', + key: 'questId', + model: QuestWeb3, + poolId: String(pool._id), + startDate, + endDate, + }), + queryQuestEntries<QuestGitcoinDocument>({ + collectionName: 'questgitcoinentry', + key: 'questId', + model: QuestGitcoin, + poolId: String(pool._id), + startDate, + endDate, + }), + ]); + + const result = { + _id: pool._id, + erc20Perks: erc20PerksQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + erc721Perks: erc721PerksQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + customRewards: customRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + couponRewards: couponRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + discordRoleRewards: discordRoleRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + galachainRewards: galachainRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + // + dailyRewards: dailyRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + milestoneRewards: milestoneRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + referralRewards: referralRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + pointRewards: pointRewardsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + web3Quests: web3QuestsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + gitcoinQuests: gitcoinQuestsQueryResult.map((x) => { + return { + day: x._id, + totalAmount: x.total_amount, + }; + }), + }; + return result; +} + +async function getPoolMetrics(pool: PoolDocument, dateRange?: { startDate: Date; endDate: Date }) { + const collections = [ + QuestDailyEntry, + QuestSocialEntry, + QuestInviteEntry, + QuestCustomEntry, + QuestWeb3Entry, + QuestGitcoinEntry, + RewardCoinPayment, + RewardNFTPayment, + RewardCustomPayment, + RewardCouponPayment, + RewardDiscordRolePayment, + RewardGalachainPayment, + ]; + const [ + dailyQuest, + socialQuest, + inviteQuest, + customQuest, + web3Quest, + gitcoinQuest, + coinReward, + nftReward, + customReward, + couponReward, + discordRoleReward, + galachainReward, + ] = await Promise.all( + collections.map(async (Model) => { + const $match = { poolId: String(pool._id) }; + if (dateRange) { + $match['createdAt'] = { $gte: dateRange.startDate, $lte: dateRange.endDate }; + } + + // Extend the $match filter with model specific properties + switch (Model) { + case QuestDailyEntry: + $match['state'] = 1; + break; + case QuestCustomEntry: + $match['isClaimed'] = true; + break; + } + + const [result] = await Model.aggregate([ + { $match }, + { + $group: { + _id: '$poolId', + totalCompleted: { $sum: 1 }, + totalAmount: { $sum: { $convert: { input: '$amount', to: 'int' } } }, + }, + }, + ]); + + const query = { poolId: String(pool._id) }; + if (dateRange) { + query['createdAt'] = { $gte: dateRange.startDate, $lte: dateRange.endDate }; + } + const totalCreated = await Model.countDocuments(query as any); + + return { + totalCompleted: result && result.totalCompleted ? result.totalCompleted : 0, + totalAmount: result && result.totalAmount ? result.totalAmount : 0, + totalCreated, + }; + }), + ); + + return { + dailyQuest, + socialQuest, + inviteQuest, + customQuest, + web3Quest, + gitcoinQuest, + coinReward, + nftReward, + customReward, + couponReward, + discordRoleReward, + galachainReward, + }; +} + +async function createLeaderboard(pool: PoolDocument, dateRange?: { startDate: Date; endDate: Date }) { + const collections = [ + QuestDailyEntry, + QuestSocialEntry, + QuestInviteEntry, + QuestCustomEntry, + QuestWeb3Entry, + QuestGitcoinEntry, + ]; + const result = await Promise.all( + collections.map(async (Model) => { + const $match = { poolId: String(pool._id) }; + + // Extend the $match filter with optional dateRange + if (dateRange) { + $match['createdAt'] = { $gte: dateRange.startDate, $lte: dateRange.endDate }; + } + + // Extend the $match filter with model specific properties + switch (Model) { + case QuestDailyEntry: + $match['state'] = 1; + break; + case QuestCustomEntry: + $match['isClaimed'] = true; + break; + } + + const $group = { + _id: '$sub', + totalCompleted: { $sum: 1 }, + totalAmount: { $sum: { $convert: { input: '$amount', to: 'int' } } }, + }; + + return await Model.aggregate([{ $match }, { $group }]); + }), + ); + + // Combine results from all collections and calculate overall totals + const walletTotals = {}; + for (const collectionResults of result) { + for (const r of collectionResults) { + if (!r) continue; + if (walletTotals[r._id]) { + walletTotals[r._id].totalCompleted += r.totalCompleted; + walletTotals[r._id].totalAmount += r.totalAmount; + } else { + walletTotals[r._id] = { + totalCompleted: r.totalCompleted, + totalAmount: r.totalAmount, + }; + } + } + } + + const wallets = await Wallet.find({ _id: Object.keys(walletTotals), sub: { $exists: true } }); + const leaderboard = wallets + .map((wallet: WalletDocument) => ({ + score: walletTotals[wallet._id].totalAmount || 0, + questEntryCount: walletTotals[wallet._id].totalCompleted || 0, + sub: wallet.sub, + })) + .filter((entry) => entry.score > 0) + .sort((a: any, b: any) => b.score - a.score); + + const updates = leaderboard.map( + (entry: { sub: string; score: number; questEntryCount: number }, index: number) => ({ + updateOne: { + filter: { poolId: String(pool._id), sub: entry.sub }, + update: { + $set: { + rank: Number(index) + 1, + score: entry.score, + questEntryCount: entry.questEntryCount, + }, + }, + }, + }), + ); + + await Participant.bulkWrite(updates); +} + +async function queryQuestEntries<T>(args: { + model: mongoose.Model<T>; + poolId: string; + collectionName: string; + key: string; + startDate: Date; + endDate: Date; + extraFilter?: object; +}) { + const extraFilter = args.extraFilter ? { ...args.extraFilter } : {}; + const queryResult = await args.model.aggregate([ + { + $match: { + poolId: args.poolId, + }, + }, + { + $lookup: { + from: args.collectionName, + let: { + id: { + $convert: { + input: '$_id', + to: 'string', + }, + }, + }, + pipeline: [ + { + $match: { + $and: [ + { + $expr: { + $eq: ['$$id', `$${args.key}`], + }, + }, + { + createdAt: { + $gte: args.startDate, + $lte: args.endDate, + }, + }, + extraFilter, + ], + }, + }, + ], + as: 'entries', + }, + }, + { + $unwind: '$entries', + }, + { + $group: { + _id: { + $dateToString: { + format: '%Y-%m-%d', + date: { $toDate: '$entries.createdAt' }, + }, + }, + total_amount: { + $sum: 1, + }, + }, + }, + ]); + + return queryResult; +} + +async function queryRewardRedemptions<T>(args: { + model: mongoose.Model<T>; + poolId: string; + collectionName: string; + key: string; + startDate: Date; + endDate: Date; + extraFilter?: object; +}) { + const extraFilter = args.extraFilter ? { ...args.extraFilter } : {}; + const queryResult = await args.model.aggregate([ + { + $match: { + poolId: args.poolId, + }, + }, + { + $lookup: { + from: args.collectionName, + let: { + id: { + $convert: { + input: '$_id', + to: 'string', + }, + }, + }, + pipeline: [ + { + $match: { + $and: [ + { + $expr: { + $eq: ['$$id', `$${args.key}`], + }, + }, + { + createdAt: { + $gte: args.startDate, + $lte: args.endDate, + }, + }, + extraFilter, + ], + }, + }, + ], + as: 'entries', + }, + }, + { + $unwind: '$entries', + }, + { + $group: { + _id: { + $dateToString: { + format: '%Y-%m-%d', + date: { $toDate: '$entries.createdAt' }, + }, + }, + total_amount: { + $sum: 1, + }, + }, + }, + ]); + + return queryResult; +} +export default { getPoolMetrics, createLeaderboard, getPoolAnalyticsForChart }; diff --git a/apps/api/src/app/services/BalancerService.ts b/apps/api/src/app/services/BalancerService.ts new file mode 100644 index 000000000..d9e6f10d2 --- /dev/null +++ b/apps/api/src/app/services/BalancerService.ts @@ -0,0 +1,246 @@ +import axios from 'axios'; +import { ethers } from 'ethers'; +import { BalancerSDK, Network } from '@balancer-labs/sdk'; +import { BALANCER_POOL_ID, ETHEREUM_RPC, HARDHAT_RPC, NODE_ENV, POLYGON_RPC } from '../config/secrets'; +import { logger } from '../util/logger'; +import { WalletDocument } from '../models'; +import { ChainId } from '@thxnetwork/common/enums'; +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { formatUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'alchemy-sdk'; + +class BalancerService { + pricing = {}; + apr = { + [ChainId.Hardhat]: { + balancer: { + min: 0, + max: 0, + swapFees: 0, + }, + thx: 0, + }, + [ChainId.Polygon]: { + balancer: { + min: 0, + max: 0, + swapFees: 0, + }, + thx: 0, + }, + }; + tvl = { + [ChainId.Hardhat]: { liquidity: '0', staked: '0', tvl: '0' }, + [ChainId.Polygon]: { liquidity: '0', staked: '0', tvl: '0' }, + }; + rewards = { + [ChainId.Hardhat]: { bal: '0', bpt: '0' }, + [ChainId.Polygon]: { bal: '0', bpt: '0' }, + }; + schedule = { + [ChainId.Hardhat]: { bal: [], bpt: [] }, + [ChainId.Polygon]: { bal: [], bpt: [] }, + }; + balancer = new BalancerSDK({ + network: Network.POLYGON, + rpcUrl: POLYGON_RPC, + }); + + constructor() { + this.updatePricesJob().then(() => { + this.updateMetricsJob(); + }); + } + + async buildJoin( + wallet: WalletDocument, + usdcAmountInWei: string, + thxAmountInWei: string, + slippage: string, + ): Promise<JoinPoolAttributes> { + const pool = await this.balancer.pools.find(BALANCER_POOL_ID); + const [usdc, thx] = pool.tokens as { + address: string; + }[]; + + return pool.buildJoin( + wallet.address, + [usdc.address, thx.address], + [usdcAmountInWei, thxAmountInWei], + slippage, + ) as JoinPoolAttributes; + } + + getPricing() { + return this.pricing; + } + + getMetrics(chainId: ChainId) { + return { + apr: this.apr[chainId], + tvl: this.tvl[chainId], + rewards: this.rewards[chainId], + schedule: this.schedule[chainId], + }; + } + + async fetchPrice(symbolIn: string, symbolOut: string) { + try { + const { data } = await axios({ + method: 'GET', + url: `https://api.coinbase.com/v2/exchange-rates?currency=${symbolIn}`, + }); + + return data.data.rates[symbolOut]; + } catch (error) { + logger.error(error); + return 0; + } + } + + async updatePricesJob() { + const pool = await this.balancer.pools.find(BALANCER_POOL_ID); + const [usdc, thx] = pool.tokens as unknown as { + symbol: string; + balance: number; + token: { latestUSDPrice: number }; + }[]; + const totalShares = pool.totalShares as unknown as number; + const thxValue = thx.balance * thx.token.latestUSDPrice; + const usdcValue = usdc.balance * usdc.token.latestUSDPrice; + const btpPrice = (thxValue + usdcValue) / totalShares; + const balPrice = await this.fetchPrice('BAL', 'USDC'); + + this.pricing = { + '20USDC-80THX': btpPrice, + 'BAL': Number(balPrice), + 'USDC': Number(usdc.token.latestUSDPrice), + 'THX': Number(thx.token.latestUSDPrice), + }; + } + + async updateMetricsJob() { + try { + const rpcMap = { [ChainId.Hardhat]: HARDHAT_RPC, [ChainId.Polygon]: POLYGON_RPC }; + const priceOfBAL = this.pricing['BAL']; + const pricePerBPT = this.pricing['20USDC-80THX']; + + // Amount of bpt-gauge locked in veTHX in wei + for (const chainId of [ChainId.Hardhat, ChainId.Polygon]) { + if (NODE_ENV === 'production' && chainId === ChainId.Hardhat) continue; + const provider = new ethers.providers.JsonRpcProvider(rpcMap[chainId]); + const gaugeAddress = contractNetworks[chainId].BPTGauge; + const bptAddress = contractNetworks[chainId].BPT; + const gauge = new ethers.Contract(gaugeAddress, contractArtifacts.BPTGauge.abi, provider); + const bpt = new ethers.Contract(bptAddress, contractArtifacts.BPT.abi, provider); + + // veTHX contract on Polygon + const veTHXAddress = contractNetworks[chainId].VotingEscrow; + const veTHX = new ethers.Contract(veTHXAddress, contractArtifacts.VotingEscrow.abi, provider); + + const { rewards, schedule } = await this.getRewards(chainId); + this.rewards[chainId] = rewards; + logger.debug(this.rewards[chainId]); + + this.schedule[chainId] = schedule; + logger.debug(this.schedule[chainId]); + + // TVL is measured as the total amount of BPT-gauge locked in veTHX + const liquidity = (await bpt.totalSupply()).toString(); + const staked = (await bpt.balanceOf(gauge.address)).toString(); + const tvl = (await gauge.balanceOf(veTHXAddress)).toString(); + this.tvl[chainId] = { liquidity, staked, tvl }; + logger.debug(this.tvl[chainId]); + + // Calc APR + const apr = await this.calculateBalancerAPR(gauge, priceOfBAL, pricePerBPT); + const balancer = { apr, swapFees: 0.2 }; // TODO Fetch swapFees from SDK or contract + const rewardsInBPT = this.rewards[chainId].bpt; + const thx = await this.calculateTHXAPR(gauge, veTHX, rewardsInBPT, pricePerBPT); + this.apr[chainId] = { balancer, thx }; + logger.debug(this.apr[chainId]); + } + + // Log pricing here because job interval creates less logging clutter + logger.debug(this.pricing); + } catch (error) { + console.log(error); + } + } + + async calculateTHXAPR(gauge: ethers.Contract, veTHX: ethers.Contract, rewardsInBPT: string, pricePerBPT: number) { + const monthlyEmissions = Number(formatUnits(rewardsInBPT, 18)); + const totalShares = Number(formatUnits(await gauge.balanceOf(veTHX.address), 18)); + const pricePerShare = pricePerBPT; + return ((monthlyEmissions * 12) / totalShares / pricePerShare) * 100; + } + + async calculateBalancerAPR(gauge: ethers.Contract, priceOfBAL: number, pricePerBPT: number) { + // Balancer Gauge contracts on Ethereum + const ethereumProvider = new ethers.providers.JsonRpcProvider(ETHEREUM_RPC); + const gaugeControllerAddress = contractNetworks[ChainId.Ethereum].BalancerGaugeController; + const gaugeController = new ethers.Contract( + gaugeControllerAddress, + contractArtifacts.BalancerGaugeController.abi, + ethereumProvider, + ); + const rootGaugeAddress = contractNetworks[ChainId.Ethereum].BalancerRootGauge; + + // APR formula inputs + const gaugeRelWeight = Number((await gaugeController.gauge_relative_weight(rootGaugeAddress)).toString()); + const workingSupply = Number((await gauge.working_supply()).toString()); + + // Take Balancer inflation schedule into account. Started at 140000 BAL per week + // https://docs.balancer.fi/concepts/governance/bal-token.html#supply-inflation-schedule + const weeklyBALemissions = 102530.48; // TODO add formula to calculate weekly emissions + + // APR formula as per + // https://docs.balancer.fi/reference/vebal-and-gauges/apr-calculation.html + + // Example data May 4th 2024 + // const workingSupply = 8.102148903933154e23; + // const gaugeRelWeight = 1518354055844830; + // const weeklyBALemissions = 102530.48; + // const priceOfBAL = 3.655; + // const pricePerBPT = 0.04489925552408662; + + return ( + (((0.4 / (workingSupply + 0.4)) * gaugeRelWeight * weeklyBALemissions * 52 * priceOfBAL) / pricePerBPT) * + 100 + ); + } + + async getRewards(chainId: ChainId) { + const rpcMap = { + [ChainId.Hardhat]: HARDHAT_RPC, + [ChainId.Polygon]: POLYGON_RPC, + }; + const { BAL, BPT, RewardFaucet, RewardDistributor } = contractNetworks[chainId]; + const provider = new ethers.providers.JsonRpcProvider(rpcMap[chainId]); + const rewardFaucet = new ethers.Contract(RewardFaucet, contractArtifacts['RewardFaucet'].abi, provider); + const amountOfWeeks = '4'; + const [balSchedule, bptSchedule] = await Promise.all( + [BAL, BPT].map(async (tokenAddress: string) => { + const upcoming = await rewardFaucet.getUpcomingRewardsForNWeeks(tokenAddress, amountOfWeeks); + return upcoming.map((amount: BigNumber) => amount.toString()); + }), + ); + const [balTotal, bptTotal] = await Promise.all( + [BAL, BPT].map(async (tokenAddress) => await rewardFaucet.totalTokenRewards(tokenAddress)), + ); + + // Add reward distributor BAL balance to the current week + const balContract = new ethers.Contract(BAL, contractArtifacts['BAL'].abi, provider); + const balBalance = await balContract.balanceOf(RewardDistributor); + balSchedule[0] = BigNumber.from(balSchedule[0]).add(balBalance).toString(); + + return { + schedule: { bal: balSchedule, bpt: bptSchedule }, + rewards: { bal: balTotal.add(balBalance).toString(), bpt: bptTotal.toString() }, + }; + } +} + +const service = new BalancerService(); + +export default service; diff --git a/apps/api/src/app/services/BrandService.ts b/apps/api/src/app/services/BrandService.ts new file mode 100644 index 000000000..bbcc60637 --- /dev/null +++ b/apps/api/src/app/services/BrandService.ts @@ -0,0 +1,10 @@ +import { Brand } from '@thxnetwork/api/models/Brand'; + +export default { + get: async (poolId: string) => { + return Brand.findOne({ poolId }); + }, + update: async (filter: Partial<TBrand>, updates: Partial<TBrand>) => { + return Brand.findOneAndUpdate(filter, updates, { upsert: true, new: true }); + }, +}; diff --git a/apps/api/src/app/services/CanvasService.ts b/apps/api/src/app/services/CanvasService.ts new file mode 100644 index 000000000..e60be467c --- /dev/null +++ b/apps/api/src/app/services/CanvasService.ts @@ -0,0 +1,79 @@ +import path from 'path'; +import { createCanvas, loadImage, registerFont } from 'canvas'; +import { assetsPath } from '@thxnetwork/api/util/path'; + +// Load on boot as registration on runtime results in font not being loaded in time +const fontPath = path.resolve(assetsPath, 'fa-solid-900.ttf'); +const family = 'Font Awesome 5 Pro Solid'; +const defaultBackgroundImgPath = path.resolve(assetsPath, 'bg.png'); +const defaultLogoImgPath = path.resolve(assetsPath, 'logo.png'); + +registerFont(fontPath, { family, style: 'normal', weight: '900' }); + +function drawImageBg(canvas, ctx, image) { + const imageAspectRatio = image.width / image.height; + let scaledWidth = canvas.width + 1, + scaledHeight = canvas.width / imageAspectRatio; + + if (scaledHeight < canvas.height) { + scaledHeight = canvas.height; + scaledWidth = canvas.height * imageAspectRatio; + } + + const offsetX = Math.floor((canvas.width - scaledWidth) / 2); + const offsetY = Math.floor((canvas.height - scaledHeight) / 2); + + // Draw mask + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(canvas.width, 0); + ctx.lineTo(canvas.width, canvas.height); + ctx.lineTo(0, canvas.height); + ctx.lineTo(0, 0); + ctx.closePath(); + + ctx.clip(); + ctx.drawImage(image, offsetX, offsetY, scaledWidth, scaledHeight); + ctx.restore(); +} + +async function dataUrlToFile(dataUrl: string) { + return await loadImage(dataUrl); +} + +async function createPreviewImage({ logoImgUrl, backgroundImgUrl }: TBrand) { + const bg = await loadImage(backgroundImgUrl || defaultBackgroundImgPath); + const logo = await loadImage(logoImgUrl || defaultLogoImgPath); + + // Create a canvas with the desired dimensions + // https://www.linkedin.com/help/linkedin/answer/a521928/make-your-website-shareable-on-linkedin + const canvasWidth = 1200; + const canvasHeight = 627; + const canvas = createCanvas(canvasWidth, canvasHeight); + + const ctx = canvas.getContext('2d'); + + // Draw the loaded image onto the canvas + drawImageBg(canvas, ctx, bg); + + // Draw the logo + const logoRatio = logo.width / logo.height; + const logoWidth = 200; + const logoHeight = logoWidth / logoRatio; + + ctx.drawImage(logo, canvasWidth / 2 - logoWidth / 2, canvasHeight / 2 - logoHeight / 2, logoWidth, logoHeight); + + // Convert the canvas content to a buffer + // const dataUrl = canvas.toDataURL('image/png'); + const buffer = canvas.toBuffer('image/png'); + + return buffer; +} + +export default { + dataUrlToFile, + createPreviewImage, + defaultBackgroundImgPath, + defaultLogoImgPath, + drawImageBg, +}; diff --git a/apps/api/src/app/services/ClaimService.ts b/apps/api/src/app/services/ClaimService.ts new file mode 100644 index 000000000..5a8eeb73e --- /dev/null +++ b/apps/api/src/app/services/ClaimService.ts @@ -0,0 +1,50 @@ +import { QRCodeEntry } from '@thxnetwork/api/models'; +import { v4 } from 'uuid'; + +export default class QRCodeService { + static create(data: Partial<TQRCodeEntry>, claimAmount: number) { + if (!claimAmount) return; + return QRCodeEntry.create(Array.from({ length: Number(claimAmount) }).map(() => ({ uuid: v4(), ...data }))); + } + + static findByReward(reward: TRewardNFT, page: number, limit: number) { + // + } +} + +// function create( +// data: { poolId: string; rewardUuid: string; erc20Id?: string; erc721Id?: string; erc1155Id?: string }, +// claimAmount: number, +// ) { +// if (!claimAmount) return; +// return QRCodeEntry.create( +// Array.from({ length: Number(claimAmount) }).map(() => ({ uuid: db.createUUID(), ...data })), +// ); +// } + +// function findByUuid(uuid: string) { +// return QRCodeEntry.findOne({ uuid }); +// } + +// function findByPool(pool: PoolDocument) { +// return QRCodeEntry.find({ poolId: String(pool._id) }); +// } + +// async function findByPerk(reward: TReward) { +// const claims = await QRCodeEntry.find({ rewardUuid: reward.uuid, poolId: reward.poolId }); +// const subs = claims.filter((c) => c.sub).map(({ sub }) => sub); +// const accounts = await AccountProxy.find({ subs }); + +// return claims +// .map((claim) => { +// return { ...claim.toJSON(), account: accounts.find((a) => claim.sub === a.sub) }; +// }) +// .sort((a, b) => { +// if (!a.sub && !b.sub) return 0; // Both are undefined, no change in order +// if (!a.sub) return -1; // a.sub is undefined, move it to the bottom +// if (!b.sub) return 1; // b.sub is undefined, move it to the bottom +// return a.sub.localeCompare(b.sub); // Sort by sub values +// }); +// } + +// export default { create, findByUuid, findByPool, findByPerk }; diff --git a/apps/api/src/app/services/ContractService.ts b/apps/api/src/app/services/ContractService.ts new file mode 100644 index 000000000..bc85cfe03 --- /dev/null +++ b/apps/api/src/app/services/ContractService.ts @@ -0,0 +1,26 @@ +import { ChainId } from '@thxnetwork/common/enums'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { SafeVersion } from '@safe-global/safe-core-sdk-types'; +import { TContractName, getArtifact } from '@thxnetwork/api/hardhat'; +import { ethers } from 'ethers'; + +export const safeVersion: SafeVersion = '1.3.0'; + +export default class ContractService { + static getChainId() { + return process.env.NODE_ENV !== 'production' ? ChainId.Hardhat : ChainId.Polygon; + } + + static getContract(contractName: TContractName, chainId: ChainId, address: string) { + const { signer } = getProvider(chainId); + const artifact = getArtifact(contractName); + return new ethers.Contract(address, artifact.abi, signer); + } + + static async deploy(contractName: TContractName, args: any[], signer: ethers.Signer): Promise<ethers.Contract> { + const { abi, bytecode } = getArtifact(contractName); + const factory = new ethers.ContractFactory(abi, bytecode, signer); + const tx = await factory.deploy(...args); + return new ethers.Contract(await tx.getAddress(), abi, signer); + } +} diff --git a/apps/api/src/app/services/DiscordService.ts b/apps/api/src/app/services/DiscordService.ts new file mode 100644 index 000000000..5a1e270e7 --- /dev/null +++ b/apps/api/src/app/services/DiscordService.ts @@ -0,0 +1,57 @@ +import { client } from '../../discord'; +import { DiscordGuild, DiscordMessage, DiscordReaction } from '../models'; +import { DiscordUser } from '../models/DiscordUser'; +import { logger } from '../util/logger'; + +export default class DiscordService { + static async getGuild(poolId: string) { + const discordGuild = await DiscordGuild.findOne({ poolId }); + if (!discordGuild) return; + try { + // Might fail if bot is removed from the guild + return await client.guilds.fetch(discordGuild.guildId); + } catch (error) { + logger.error(error); + } + } + + static async getMember(guildId: string, userId: string) { + try { + // Might fail if bot is removed from the guild + return await client.guilds.fetch(guildId).then((guild) => guild.members.fetch(userId)); + } catch (error) { + logger.error(error); + } + } + + static async getRole(guildId: string, roleId: string) { + try { + return await client.guilds.fetch(guildId).then((guild) => guild.roles.fetch(roleId)); + } catch (error) { + logger.error(error); + } + } + + static async getUserMetrics(poolId: string, userId: string) { + const guild = await this.getGuild(poolId); + if (!guild) return; + + const member = await this.getMember(guild.id, userId); + if (!member) return; + + const profileImgUrl = member.user.displayAvatarURL({ forceStatic: true }); + const query = { guildId: guild.id, userId }; + + return await DiscordUser.create({ + userId, + guildId: guild.id, + profileImgUrl, + username: member.user.username, + publicMetrics: { + joinedAt: new Date(member.joinedTimestamp).toISOString(), + reactionCount: guild ? await DiscordReaction.countDocuments(query) : 0, + messageCount: guild ? await DiscordMessage.countDocuments(query) : 0, + }, + }); + } +} diff --git a/apps/api/src/app/services/ERC1155Service.ts b/apps/api/src/app/services/ERC1155Service.ts new file mode 100644 index 000000000..3d9be0081 --- /dev/null +++ b/apps/api/src/app/services/ERC1155Service.ts @@ -0,0 +1,348 @@ +import { keccak256, toUtf8Bytes } from 'ethers/lib/utils'; +import { TransactionReceipt } from 'web3-core'; +import { ChainId, TransactionState, ERC1155TokenState } from '@thxnetwork/common/enums'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { paginatedResults } from '@thxnetwork/api/util/pagination'; +import { assertEvent, ExpectedEventNotFound, findEvent, parseLogs } from '@thxnetwork/api/util/events'; +import { API_URL, VERSION } from '../config/secrets'; +import TransactionService from './TransactionService'; +import PoolService from './PoolService'; +import IPFSService from './IPFSService'; +import SafeService from './SafeService'; +import { + Transaction, + ERC1155Document, + ERC1155, + PoolDocument, + RewardNFT, + ERC1155MetadataDocument, + ERC1155Metadata, + WalletDocument, + ERC1155TokenDocument, + ERC1155Token, +} from '@thxnetwork/api/models'; +import { getArtifact } from '../hardhat'; + +const contractName = 'THXERC1155'; + +async function deploy(data: TERC1155, forceSync = true): Promise<ERC1155Document> { + const { web3, defaultAccount } = getProvider(data.chainId); + const { abi, bytecode } = getArtifact(contractName); + const contract = new web3.eth.Contract(abi); + const erc1155 = await ERC1155.create(data); + const baseURL = getBaseURL(erc1155); + const fn = contract.deploy({ + data: bytecode, + arguments: [baseURL, defaultAccount], + }); + + const txId = await TransactionService.sendAsync(null, fn, erc1155.chainId, forceSync, { + type: 'ERC1155DeployCallback', + args: { erc1155Id: String(erc1155._id) }, + }); + + return ERC1155.findByIdAndUpdate(erc1155._id, { transactions: [txId], baseURL }, { new: true }); +} + +async function deployCallback({ erc1155Id }: TERC1155DeployCallbackArgs, receipt: TransactionReceipt) { + const { abi } = getArtifact(contractName); + const events = parseLogs(abi, receipt.logs); + + if (!findEvent('OwnershipTransferred', events) && !findEvent('Transfer', events)) { + throw new ExpectedEventNotFound('Transfer or OwnershipTransferred'); + } + + await ERC1155.findByIdAndUpdate(erc1155Id, { address: receipt.contractAddress }); +} + +export async function queryDeployTransaction(erc1155: ERC1155Document): Promise<ERC1155Document> { + if (!erc1155.address && erc1155.transactions[0]) { + const tx = await Transaction.findById(erc1155.transactions[0]); + const txResult = await TransactionService.queryTransactionStatusReceipt(tx); + if (txResult === TransactionState.Mined) { + erc1155 = await findById(erc1155._id); + } + } + + return erc1155; +} + +function getBaseURL(erc1155: ERC1155Document) { + return `${API_URL}/${VERSION}/metadata/erc1155/${String(erc1155._id)}/{id}`; +} + +const initialize = async (pool: PoolDocument, address: string) => { + const erc1155 = await findByQuery({ address, chainId: pool.chainId }); + await addMinter(erc1155, pool.safeAddress); +}; + +export async function findById(id: string): Promise<ERC1155Document> { + const erc1155 = await ERC1155.findById(id); + if (!erc1155) return; + erc1155.logoImgUrl || erc1155.logoImgUrl || `https://api.dicebear.com/7.x/identicon/svg?seed=${erc1155.address}`; + return erc1155; +} + +export async function findBySub(sub: string): Promise<ERC1155Document[]> { + const pools = await PoolService.getAllBySub(sub); + const nftRewards = await RewardNFT.find({ poolId: pools.map((p) => String(p._id)) }); + const erc1155Ids = nftRewards.map((c) => c.erc1155Id); + const erc1155s = await ERC1155.find({ sub }); + + return erc1155s.concat(await ERC1155.find({ _id: erc1155Ids })); +} + +export async function createMetadata(erc1155: ERC1155Document, attributes: any): Promise<ERC1155MetadataDocument> { + return ERC1155Metadata.create({ + erc1155: String(erc1155._id), + attributes, + }); +} + +export async function deleteMetadata(id: string) { + return ERC1155Metadata.findOneAndDelete({ _id: id }); +} + +export async function mint( + safe: WalletDocument, + erc1155: ERC1155Document, + wallet: WalletDocument, + metadata: ERC1155MetadataDocument, + amount: string, +): Promise<ERC1155TokenDocument> { + const tokenUri = await IPFSService.getTokenURI(erc1155, String(metadata._id), String(metadata.tokenId)); + const erc1155token = await ERC1155Token.findOneAndUpdate( + { + erc1155Id: String(erc1155._id), + tokenId: metadata.tokenId, + sub: wallet.sub, + walletId: String(wallet._id), + }, + { + sub: wallet.sub, + tokenUri: erc1155.baseURL.replace('{id}', tokenUri), + recipient: wallet.address, + state: ERC1155TokenState.Pending, + erc1155Id: String(erc1155._id), + metadataId: String(metadata._id), + walletId: String(wallet._id), + tokenId: metadata.tokenId, + }, + { upsert: true, new: true }, + ); + + const tx = await TransactionService.sendSafeAsync( + safe, + erc1155.address, + erc1155.contract.methods.mint(wallet.address, metadata.tokenId, amount, '0x'), + { + type: 'erc1155TokenMintCallback', + args: { erc1155tokenId: String(erc1155token._id) }, + }, + ); + + return await ERC1155Token.findByIdAndUpdate( + erc1155token._id, + { transactions: [tx._id], state: ERC1155TokenState.Transferring }, + { new: true }, + ); +} + +export async function mintCallback(args: TERC1155TokenMintCallbackArgs, receipt: TransactionReceipt) { + const { erc1155tokenId } = args; + const { abi } = getArtifact(contractName); + const events = parseLogs(abi, receipt.logs); + const event = assertEvent('TransferSingle', events); + + await ERC1155Token.findByIdAndUpdate(erc1155tokenId, { + state: ERC1155TokenState.Minted, + tokenId: event.args.id, + recipient: event.args.recipient, + }); +} + +export async function queryMintTransaction(erc1155Token: ERC1155TokenDocument): Promise<ERC1155TokenDocument> { + if (erc1155Token.state === ERC1155TokenState.Pending && erc1155Token.transactions[0]) { + const tx = await Transaction.findById(erc1155Token.transactions[0]); + const txResult = await TransactionService.queryTransactionStatusReceipt(tx); + if (txResult === TransactionState.Mined) { + erc1155Token = await findTokenById(erc1155Token._id); + } + } + return erc1155Token; +} + +export async function transferFrom( + erc1155: ERC1155Document, + wallet: WalletDocument, + to: string, + erc1155Token: ERC1155TokenDocument, + amount: string, +): Promise<ERC1155TokenDocument> { + const toWallet = await SafeService.findOne({ address: to, chainId: erc1155.chainId }); + const tx = await TransactionService.sendSafeAsync( + wallet, + erc1155.address, + erc1155.contract.methods.safeTransferFrom(wallet.address, to, erc1155Token.tokenId, amount, '0x'), + { + type: 'erc1155TransferFromCallback', + args: { + erc1155Id: String(erc1155._id), + erc1155TokenId: String(erc1155Token._id), + walletId: toWallet && toWallet.id, + }, + }, + ); + const metadata = await ERC1155Metadata.findById(erc1155Token.metadataId); + + await ERC1155Token.findOneAndUpdate( + { + erc1155Id: String(erc1155._id), + tokenId: metadata.tokenId, + sub: wallet.sub, + walletId: String(wallet._id), + }, + { + sub: wallet.sub, + tokenUri: erc1155.baseURL.replace('{id}', erc1155Token.tokenUri), + recipient: wallet.address, + state: ERC1155TokenState.Pending, + erc1155Id: String(erc1155._id), + metadataId: String(metadata._id), + walletId: String(wallet._id), + tokenId: metadata.tokenId, + }, + { upsert: true, new: true }, + ); + + return await ERC1155Token.findByIdAndUpdate( + erc1155Token._id, + { transactions: [String(tx._id)], state: ERC1155TokenState.Transferring }, + { new: true }, + ); +} + +export async function transferFromCallback(args: TERC1155TransferFromCallbackArgs, receipt: TransactionReceipt) { + const { erc1155TokenId, walletId } = args; + const { abi } = getArtifact(contractName); + const events = parseLogs(abi, receipt.logs); + const event = assertEvent('TransferSingle', events); + const wallet = await SafeService.findById(walletId); + + await ERC1155Token.findByIdAndUpdate(erc1155TokenId, { + state: ERC1155TokenState.Transferred, + tokenId: event.args.id, + recipient: event.args.to, + sub: wallet && wallet.sub, + walletId: wallet && wallet.id, + }); +} + +async function isMinter(erc1155: ERC1155Document, address: string) { + return await erc1155.contract.methods.hasRole(keccak256(toUtf8Bytes('MINTER_ROLE')), address).call(); +} + +async function addMinter(erc1155: ERC1155Document, address: string) { + const receipt = await TransactionService.send( + erc1155.address, + erc1155.contract.methods.grantRole(keccak256(toUtf8Bytes('MINTER_ROLE')), address), + erc1155.chainId, + ); + + assertEvent('RoleGranted', parseLogs(erc1155.contract.options.jsonInterface, receipt.logs)); +} + +async function findMetadataByToken(token: TERC1155Token) { + return ERC1155Metadata.findById(token.metadataId); +} + +async function findTokenById(id: string): Promise<ERC1155TokenDocument> { + return ERC1155Token.findById(id); +} + +async function findTokensByMetadataAndSub(metadataId: string, account: TAccount): Promise<ERC1155TokenDocument[]> { + return ERC1155Token.find({ sub: account.sub, metadataId }); +} + +async function findTokensBySub(sub: string): Promise<ERC1155TokenDocument[]> { + return ERC1155Token.find({ sub }); +} + +async function findTokensByWallet(wallet: WalletDocument): Promise<ERC1155TokenDocument[]> { + return ERC1155Token.find({ walletId: wallet._id }); +} + +async function findMetadataById(id: string) { + return ERC1155Metadata.findById(id); +} + +async function findTokensByRecipient(recipient: string, erc1155Id: string): Promise<TERC1155Token[]> { + const result = []; + for await (const token of ERC1155Token.find({ recipient, erc1155Id })) { + const metadata = await ERC1155Metadata.findById(token.metadataId); + result.push({ ...(token.toJSON() as TERC1155Token), metadata }); + } + return result; +} + +async function findTokensByMetadata(metadata: ERC1155MetadataDocument): Promise<TERC1155Token[]> { + return ERC1155Token.find({ metadataId: String(metadata._id) }); +} + +async function findMetadataByNFT(erc1155Id: string, page = 1, limit = 10) { + const paginatedResult = await paginatedResults(ERC1155Metadata, page, limit, { erc1155Id }); + const results: TERC1155Metadata[] = []; + for (const metadata of paginatedResult.results) { + const tokens = (await this.findTokensByMetadata(metadata)).map((m: ERC1155MetadataDocument) => m.toJSON()); + results.push({ ...metadata.toJSON(), tokens }); + } + paginatedResult.results = results; + return paginatedResult; +} + +async function findByQuery(query: { poolAddress?: string; address?: string; chainId?: ChainId }) { + return ERC1155.findOne(query); +} + +export const update = (erc1155: ERC1155Document, updates: Partial<TERC1155>) => { + return ERC1155.findByIdAndUpdate(erc1155._id, updates, { new: true }); +}; + +export const getOnChainERC1155Token = async (chainId: number, address: string) => { + const { web3 } = getProvider(chainId); + const { abi } = getArtifact(contractName); + const contract = new web3.eth.Contract(abi, address); + const uri = await contract.methods.uri(1).call(); + + return { uri }; +}; + +export default { + deploy, + deployCallback, + findById, + createMetadata, + deleteMetadata, + mint, + mintCallback, + queryMintTransaction, + findBySub, + findTokenById, + findTokensByMetadataAndSub, + findTokensByMetadata, + findTokensBySub, + findMetadataById, + findMetadataByNFT, + findTokensByRecipient, + findByQuery, + addMinter, + isMinter, + update, + initialize, + transferFrom, + transferFromCallback, + queryDeployTransaction, + getOnChainERC1155Token, + findTokensByWallet, + findMetadataByToken, +}; diff --git a/apps/api/src/app/services/ERC20Service.ts b/apps/api/src/app/services/ERC20Service.ts new file mode 100644 index 000000000..d21afb21d --- /dev/null +++ b/apps/api/src/app/services/ERC20Service.ts @@ -0,0 +1,341 @@ +import { toChecksumAddress } from 'web3-utils'; +import { assertEvent, ExpectedEventNotFound, findEvent, parseLogs } from '@thxnetwork/api/util/events'; +import { ChainId, ERC20Type, TransactionState } from '@thxnetwork/common/enums'; +import { keccak256, toUtf8Bytes } from 'ethers/lib/utils'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { TransactionReceipt } from 'web3-core'; +import { contractNetworks, getArtifact } from '@thxnetwork/api/hardhat'; +import { + ERC20, + ERC20Document, + ERC20Token, + ERC20TokenDocument, + ERC20Transfer, + PoolDocument, + RewardCoin, + Transaction, + Wallet, + WalletDocument, +} from '@thxnetwork/api/models'; +import TransactionService from './TransactionService'; +import PoolService from './PoolService'; +import { fromWei } from 'web3-utils'; + +async function decorate(token: ERC20TokenDocument, wallet: WalletDocument) { + const erc20 = await getById(token.erc20Id); + if (!erc20 || erc20.chainId !== wallet.chainId) return; + + const walletBalanceInWei = await erc20.contract.methods.balanceOf(wallet.address).call(); + const walletBalance = fromWei(walletBalanceInWei, 'ether'); + + return Object.assign(token.toJSON() as TERC20Token, { + walletBalance, + erc20, + }); +} + +function getDeployArgs(erc20: ERC20Document, totalSupply?: string) { + const { defaultAccount } = getProvider(erc20.chainId); + + switch (erc20.type) { + case ERC20Type.Limited: { + return [erc20.name, erc20.symbol, defaultAccount, totalSupply]; + } + case ERC20Type.Unlimited: { + return [erc20.name, erc20.symbol, defaultAccount]; + } + } +} + +export async function findBySub(sub: string) { + const pools = await PoolService.getAllBySub(sub); + const coinRewards = await RewardCoin.find({ poolId: pools.map((p) => String(p._id)) }); + const erc20Ids = coinRewards.map((c) => c.erc20Id); + const erc20s = await ERC20.find({ sub }); + + return erc20s.concat(await ERC20.find({ _id: erc20Ids })); +} + +export const deploy = async (params: Partial<TERC20>, forceSync = true) => { + const erc20 = await ERC20.create({ + name: params.name, + symbol: params.symbol, + chainId: params.chainId, + type: params.type, + sub: params.sub, + logoImgUrl: params.logoImgUrl, + }); + const { web3 } = getProvider(erc20.chainId); + const { abi, bytecode } = getArtifact(erc20.contractName); + const contract = new web3.eth.Contract(abi); + const fn = contract.deploy({ + data: bytecode, + arguments: getDeployArgs(erc20, String(params.totalSupply)), + }); + + const txId = await TransactionService.sendAsync(null, fn, erc20.chainId, forceSync, { + type: 'Erc20DeployCallback', + args: { erc20Id: String(erc20._id) }, + }); + + return ERC20.findByIdAndUpdate(erc20._id, { transactions: [txId] }, { new: true }); +}; + +export async function deployCallback({ erc20Id }: TERC20DeployCallbackArgs, receipt: TransactionReceipt) { + const erc20 = await ERC20.findById(erc20Id); + const { abi } = getArtifact(erc20.contractName); + const events = parseLogs(abi, receipt.logs); + + // Limited and unlimited tokes emit different events. Check if one of the two is emitted. + if (!findEvent('OwnershipTransferred', events) && !findEvent('Transfer', events)) { + throw new ExpectedEventNotFound('Transfer or OwnershipTransferred'); + } + + await ERC20.findByIdAndUpdate(erc20Id, { + address: receipt.contractAddress, + }); +} + +export async function queryDeployTransaction(erc20: ERC20Document): Promise<ERC20Document> { + if (!erc20.address && erc20.transactions[0]) { + const tx = await Transaction.findById(erc20.transactions[0]); + const txResult = await TransactionService.queryTransactionStatusReceipt(tx); + if (txResult === TransactionState.Mined) { + erc20 = await getById(erc20._id); + } + } + + return erc20; +} + +const initialize = async (pool: PoolDocument, erc20: ERC20Document) => { + if (erc20 && erc20.type === ERC20Type.Unlimited) { + await addMinter(erc20, pool.safeAddress); + } +}; + +const addMinter = async (erc20: ERC20Document, address: string) => { + const receipt = await TransactionService.send( + erc20.address, + erc20.contract.methods.grantRole(keccak256(toUtf8Bytes('MINTER_ROLE')), address), + erc20.chainId, + ); + + assertEvent('RoleGranted', parseLogs(erc20.contract.options.jsonInterface, receipt.logs)); +}; + +const addToken = async (wallet: WalletDocument, erc20: ERC20Document) => { + const query = { sub: wallet.sub, walletId: wallet.id, erc20Id: erc20._id }; + if (!(await ERC20Token.exists(query))) { + await createERC20Token(erc20, wallet); + } +}; + +export const getAll = (sub: string) => { + return ERC20.find({ sub }); +}; + +export const getTokensForSub = (sub: string) => { + return ERC20Token.find({ sub }); +}; + +export const getTokensForWallet = async (wallet: WalletDocument) => { + const tokens = await ERC20Token.find({ walletId: wallet.id }); + + const result = []; + for (const token of tokens) { + try { + const decorated = await decorate(token, wallet); + result.push(decorated); + } catch (error) { + console.log(error); + } + } + + const defaultTokens = (await findDefaultTokens(wallet)).filter(({ walletBalance }) => walletBalance > 0); + + return result.concat(defaultTokens); +}; + +export const getById = async (id: string) => { + const erc20 = await ERC20.findById(id); + if (!erc20) return; + + erc20.logoImgUrl = erc20.logoImgUrl || `https://api.dicebear.com/7.x/identicon/svg?seed=${erc20.address}`; + return erc20; +}; + +export const getTokenById = (id: string) => { + return ERC20Token.findById(id); +}; + +export const findBy = (query: { address: string; chainId: ChainId; sub?: string }) => { + return ERC20.findOne(query); +}; + +export const addTokenForWallet = async (erc20: ERC20Document, wallet: WalletDocument) => { + const hasToken = await ERC20Token.exists({ + sub: wallet.sub, + walletId: wallet.id, + erc20Id: erc20.id, + }); + + if (!hasToken) { + await createERC20Token(erc20, wallet); + } +}; + +export const importToken = async (chainId: number, address: string, sub: string, logoImgUrl: string) => { + const { web3 } = getProvider(chainId); + const { abi } = getArtifact('THXERC20_LimitedSupply'); + const contract = new web3.eth.Contract(abi); + const [name, symbol] = await Promise.all([contract.methods.name().call(), contract.methods.symbol().call()]); + const erc20 = await ERC20.create({ + name, + symbol, + address: toChecksumAddress(address), + chainId, + type: ERC20Type.Unknown, + sub, + logoImgUrl, + }); + + const wallets = await Wallet.find({ sub }); + for (const wallet of wallets) { + await addTokenForWallet(erc20, wallet); + } + + return erc20; +}; + +export const update = (erc20: ERC20Document, updates: Partial<TERC20>) => { + return ERC20.findByIdAndUpdate(erc20._id, updates, { new: true }); +}; + +export const approve = async (erc20: ERC20Document, wallet: WalletDocument, amountInWei: string) => { + return await TransactionService.sendSafeAsync( + wallet, + erc20.address, + erc20.contract.methods.approve(wallet.address, amountInWei), + ); +}; + +export const transferFrom = async (erc20: ERC20Document, wallet: WalletDocument, to: string, amountInWei: string) => { + const erc20Transfer = await ERC20Transfer.create({ + erc20Id: erc20._id, + from: wallet.address, + to, + amount: amountInWei, + chainId: wallet.chainId, + sub: wallet.sub, + }); + + // Check if an erc20Token exists for a known receiving wallet and create one if not + const toWallet = await Wallet.findOne({ chainId: wallet.chainId, address: toChecksumAddress(to) }); + if (toWallet && !(await ERC20Token.exists({ walletId: toWallet._id, erc20Id: erc20._id }))) { + await createERC20Token(erc20, toWallet); + } + + const tx = await TransactionService.sendSafeAsync( + wallet, + erc20.address, + erc20.contract.methods.transfer(to, amountInWei), + { type: 'transferFromCallBack', args: { erc20Id: String(erc20._id) } }, + ); + + await erc20Transfer.updateOne({ transactionId: String(tx._id) }); + + return tx; +}; + +export const transferFromCallBack = async (args: TERC20TransferFromCallBackArgs, receipt: TransactionReceipt) => { + const erc20 = await ERC20.findById(args.erc20Id); + const events = parseLogs(erc20.contract.options.jsonInterface, receipt.logs); + + assertEvent('ERC20ProxyTransferFrom', events); +}; + +async function isMinter(erc20: ERC20Document, address: string) { + return await erc20.contract.methods.hasRole(keccak256(toUtf8Bytes('MINTER_ROLE')), address).call(); +} + +async function createERC20Token(erc20: ERC20Document, wallet: WalletDocument) { + await ERC20Token.create({ + sub: wallet.sub, + walletId: wallet.id, + erc20Id: erc20.id, + }); +} + +async function findDefaultTokens(wallet: WalletDocument) { + const defaultContracts = [ + { + type: ERC20Type.Unknown, + name: '20USDC-80THX', + symbol: '20USDC-80THX', + decimals: 18, + chainId: wallet.chainId, + address: contractNetworks[wallet.chainId].BPT, + logoImgUrl: 'https://assets.coingecko.com/coins/images/21323/standard/logo-thx-resized-200-200.png', + }, + { + type: ERC20Type.Unknown, + name: '20USDC-80THX (staked)', + symbol: '20USDC-80THX-gauge', + decimals: 18, + chainId: wallet.chainId, + address: contractNetworks[wallet.chainId].BPTGauge, + logoImgUrl: 'https://assets.coingecko.com/coins/images/21323/standard/logo-thx-resized-200-200.png', + }, + { + type: ERC20Type.Unknown, + name: 'Voting Escrow 20USDC-80THX-gauge', + symbol: 'veTHX', + decimals: 18, + chainId: wallet.chainId, + address: contractNetworks[wallet.chainId].VotingEscrow, + logoImgUrl: 'https://assets.coingecko.com/coins/images/21323/standard/logo-thx-resized-200-200.png', + }, + ]; + + const promises = defaultContracts.map(async (erc20) => { + const { web3 } = getProvider(erc20.chainId); + const { abi } = getArtifact('THXERC20_LimitedSupply'); + const contract = new web3.eth.Contract(abi, erc20.address); + const walletBalanceInWei = await contract.methods.balanceOf(wallet.address).call(); + const walletBalance = Number(fromWei(walletBalanceInWei)); + return { + sub: wallet.sub, + erc20Id: '', + walletId: wallet.id, + walletBalance, + erc20, + }; + }); + + return await Promise.all(promises); +} + +export default { + findDefaultTokens, + decorate, + findBySub, + createERC20Token, + deploy, + getAll, + findBy, + getById, + addToken, + addMinter, + isMinter, + importToken, + getTokensForSub, + getTokenById, + update, + initialize, + queryDeployTransaction, + transferFrom, + transferFromCallBack, + getTokensForWallet, + approve, +}; diff --git a/apps/api/src/app/services/ERC721Service.ts b/apps/api/src/app/services/ERC721Service.ts new file mode 100644 index 000000000..21a529a29 --- /dev/null +++ b/apps/api/src/app/services/ERC721Service.ts @@ -0,0 +1,289 @@ +import { keccak256, toUtf8Bytes } from 'ethers/lib/utils'; +import { TransactionReceipt } from 'web3-core'; +import { ERC721, ERC721Document } from '@thxnetwork/api/models/ERC721'; +import { ERC721Metadata, ERC721MetadataDocument } from '@thxnetwork/api/models/ERC721Metadata'; +import { ERC721Token, ERC721TokenDocument } from '@thxnetwork/api/models/ERC721Token'; +import { Transaction } from '@thxnetwork/api/models/Transaction'; +import { ERC721TokenState, TransactionState } from '@thxnetwork/common/enums'; +import { assertEvent, ExpectedEventNotFound, findEvent, parseLogs } from '@thxnetwork/api/util/events'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { paginatedResults } from '@thxnetwork/api/util/pagination'; +import { WalletDocument } from '../models/Wallet'; +import { RewardNFT } from '../models/RewardNFT'; +import { getArtifact } from '../hardhat'; +import PoolService from './PoolService'; +import TransactionService from './TransactionService'; +import IPFSService from './IPFSService'; +import WalletService from './WalletService'; + +const contractName = 'THXERC721'; + +async function deploy(data: TERC721, forceSync = true): Promise<ERC721Document> { + const { web3, defaultAccount } = getProvider(data.chainId); + const { abi, bytecode } = getArtifact(contractName); + const contract = new web3.eth.Contract(abi); + const erc721 = await ERC721.create(data); + const fn = contract.deploy({ + data: bytecode, + arguments: [erc721.name, erc721.symbol, erc721.baseURL, defaultAccount], + }); + const txId = await TransactionService.sendAsync(null, fn, erc721.chainId, forceSync, { + type: 'Erc721DeployCallback', + args: { erc721Id: String(erc721._id) }, + }); + + return await ERC721.findByIdAndUpdate(erc721._id, { transactions: [txId] }, { new: true }); +} + +async function deployCallback({ erc721Id }: TERC721DeployCallbackArgs, receipt: TransactionReceipt) { + const { abi } = getArtifact(contractName); + const events = parseLogs(abi, receipt.logs); + + if (!findEvent('OwnershipTransferred', events) && !findEvent('Transfer', events)) { + throw new ExpectedEventNotFound('Transfer or OwnershipTransferred'); + } + + await ERC721.findByIdAndUpdate(erc721Id, { address: receipt.contractAddress }); +} + +export async function queryDeployTransaction(erc721: ERC721Document): Promise<ERC721Document> { + if (!erc721.address && erc721.transactions[0]) { + const tx = await Transaction.findById(erc721.transactions[0]); + const txResult = await TransactionService.queryTransactionStatusReceipt(tx); + if (txResult === TransactionState.Mined) { + erc721 = await findById(erc721._id); + } + } + + return erc721; +} + +export async function findById(id: string): Promise<ERC721Document> { + const erc721 = await ERC721.findById(id); + if (!erc721) return; + erc721.logoImgUrl = erc721.logoImgUrl || `https://api.dicebear.com/7.x/identicon/svg?seed=${erc721.address}`; + return erc721; +} + +export async function findBySub(sub: string): Promise<ERC721Document[]> { + const pools = await PoolService.getAllBySub(sub); + const nftRewards = await RewardNFT.find({ poolId: pools.map((p) => String(p._id)) }); + const erc721Ids = nftRewards.map((c) => c.erc721Id); + const erc721s = await ERC721.find({ sub }); + + return erc721s.concat(await ERC721.find({ _id: erc721Ids })); +} + +export async function deleteMetadata(id: string) { + return ERC721Metadata.findOneAndDelete({ _id: id }); +} + +export async function mint( + safe: WalletDocument, + erc721: ERC721Document, + wallet: WalletDocument, + metadata: ERC721MetadataDocument, +): Promise<ERC721TokenDocument> { + const tokenUri = await IPFSService.getTokenURI(erc721, String(metadata._id)); + const erc721token = await ERC721Token.create({ + sub: wallet.sub, + tokenUri: erc721.baseURL + tokenUri, + recipient: wallet.address, + state: ERC721TokenState.Pending, + erc721Id: String(erc721._id), + metadataId: String(metadata._id), + walletId: wallet._id, + }); + + const tx = await TransactionService.sendSafeAsync( + safe, + erc721.address, + erc721.contract.methods.mint(wallet.address, tokenUri), + { + type: 'erc721TokenMintCallback', + args: { erc721tokenId: String(erc721token._id) }, + }, + ); + + return await ERC721Token.findByIdAndUpdate( + erc721token._id, + { transactions: [String(tx._id)], state: ERC721TokenState.Transferring }, + { new: true }, + ); +} + +export async function mintCallback(args: TERC721TokenMintCallbackArgs, receipt: TransactionReceipt) { + const token = await ERC721Token.findById(args.erc721tokenId); + const { contract } = await ERC721.findById(token.erc721Id); + const events = parseLogs(contract.options.jsonInterface, receipt.logs); + const event = assertEvent('Transfer', events); + + await token.updateOne({ + state: ERC721TokenState.Minted, + tokenId: Number(event.args.tokenId), + recipient: event.args.to, + }); +} + +export async function queryMintTransaction(erc721Token: ERC721TokenDocument): Promise<ERC721TokenDocument> { + if (erc721Token.state === ERC721TokenState.Pending && erc721Token.transactions[0]) { + const tx = await Transaction.findById(erc721Token.transactions[0]); + const txResult = await TransactionService.queryTransactionStatusReceipt(tx); + if (txResult === TransactionState.Mined) { + erc721Token = await ERC721Token.findById(erc721Token._id); + } + } + + return erc721Token; +} + +export function parseAttributes(entry: ERC721MetadataDocument) { + return { + name: entry.name, + description: entry.description, + image: entry.image, + external_url: entry.externalUrl, + }; +} + +async function isMinter(erc721: ERC721Document, address: string) { + return await erc721.contract.methods.hasRole(keccak256(toUtf8Bytes('MINTER_ROLE')), address).call(); +} + +async function addMinter(erc721: ERC721Document, address: string) { + const receipt = await TransactionService.send( + erc721.address, + erc721.contract.methods.grantRole(keccak256(toUtf8Bytes('MINTER_ROLE')), address), + erc721.chainId, + ); + + assertEvent('RoleGranted', parseLogs(erc721.contract.options.jsonInterface, receipt.logs)); +} + +async function findTokensByRecipient(recipient: string, erc721Id: string): Promise<TERC721Token[]> { + const result = []; + for await (const token of ERC721Token.find({ recipient, erc721Id })) { + const metadata = await ERC721Metadata.findById(token.metadataId); + result.push({ ...(token.toJSON() as TERC721Token), metadata }); + } + return result; +} + +async function findMetadataByToken(token: TERC721Token) { + return ERC721Metadata.findById(token.metadataId); +} + +async function findTokenById(id: string) { + return await ERC721Token.findById(id); +} + +async function findMetadataById(id: string) { + return await ERC721Metadata.findById(id); +} + +async function findMetadataByNFT(erc721Id: string, page = 1, limit = 10) { + const paginatedResult = await paginatedResults(ERC721Metadata, page, limit, { erc721Id }); + const results: TERC721Metadata[] = []; + for (const metadata of paginatedResult.results) { + const tokens = await ERC721Token.find({ erc721Id, metadataId: metadata._id }); + results.push({ ...metadata.toJSON(), tokens }); + } + paginatedResult.results = results; + return paginatedResult; +} + +export const getOnChainERC721Token = async (chainId: number, address: string) => { + const { web3 } = getProvider(chainId); + const { abi } = getArtifact(contractName); + const contract = new web3.eth.Contract(abi, address); + const [name, symbol, totalSupply] = await Promise.all([ + contract.methods.name().call(), + contract.methods.symbol().call(), + contract.methods.totalSupply().call(), + ]); + + return { name, symbol, totalSupply }; +}; + +export async function transferFrom( + erc721: ERC721Document, + wallet: WalletDocument, + to: string, + erc721Token: ERC721TokenDocument, +): Promise<ERC721TokenDocument> { + const toWallet = await WalletService.findOne({ address: to, chainId: erc721.chainId }); + const tx = await TransactionService.sendSafeAsync( + wallet, + erc721.address, + erc721.contract.methods.transferFrom(wallet.address, to, erc721Token.tokenId), + { + type: 'erc721nTransferFromCallback', + args: { + erc721Id: String(erc721._id), + erc721TokenId: String(erc721Token._id), + walletId: toWallet && toWallet._id, + }, + }, + ); + return await ERC721Token.findByIdAndUpdate( + erc721Token._id, + { + transactions: [String(tx._id)], + state: ERC721TokenState.Transferring, + }, + { new: true }, + ); +} + +export async function transferFromCallback(args: TERC721TransferFromCallBackArgs, receipt: TransactionReceipt) { + const { erc721TokenId, walletId } = args; + const erc721Token = await ERC721Token.findById(erc721TokenId); + const erc721 = await ERC721.findById(erc721Token.erc721Id); + const events = parseLogs(erc721.contract.options.jsonInterface, receipt.logs); + const event = assertEvent('Transfer', events); + const wallet = await WalletService.findById(walletId); + + await erc721Token.updateOne({ + state: ERC721TokenState.Transferred, + tokenId: Number(event.args.tokenId), + recipient: event.args.to, + sub: wallet && wallet.sub, + walletId: wallet && String(wallet._id), + }); +} + +export async function queryTransferFromTransaction(erc721Token: ERC721TokenDocument): Promise<ERC721TokenDocument> { + if (erc721Token.state === ERC721TokenState.Transferring) { + const tx = await Transaction.findById(erc721Token.transactions[erc721Token.transactions.length - 1]); + const txResult = await TransactionService.queryTransactionStatusReceipt(tx); + if (txResult === TransactionState.Mined) { + erc721Token = await ERC721Token.findById(erc721Token._id); + } + } + + return erc721Token; +} + +export default { + deploy, + deployCallback, + findById, + deleteMetadata, + mint, + mintCallback, + queryMintTransaction, + findBySub, + findMetadataByNFT, + findTokensByRecipient, + addMinter, + isMinter, + parseAttributes, + queryDeployTransaction, + getOnChainERC721Token, + transferFrom, + transferFromCallback, + queryTransferFromTransaction, + findMetadataById, + findTokenById, + findMetadataByToken, +}; diff --git a/apps/api/src/app/services/GalachainService.ts b/apps/api/src/app/services/GalachainService.ts new file mode 100644 index 000000000..228f55a20 --- /dev/null +++ b/apps/api/src/app/services/GalachainService.ts @@ -0,0 +1,205 @@ +import axios from 'axios'; +import { instanceToPlain, plainToInstance } from 'class-transformer'; +import { BigNumber } from 'bignumber.js'; +import { logger } from '../util/logger'; +import { BadRequestError } from '../util/errors'; +import { + ChainCallDTO, + TokenInstance, + TokenClassKey, + TokenInstanceKey, + createValidDTO, + CreateTokenClassDto, + GalaChainResponse, + GetMyProfileDto, + MintTokenDto, + GrantAllowanceDto, + AllowanceType, + FetchBalancesDto, + TransferTokenDto, + RegisterUserDto, +} from '@gala-chain/api'; +import { GalachainRole, getClient } from '../util/galachain'; +import { Wallet } from 'ethers'; +import { NODE_ENV } from '../config/secrets'; + +const GALACHAIN_URL = 'https://gateway.stage.galachain.com/api'; +const identityKey = (address: string) => `eth|${address.replace(/^0x/, '')}`; + +export default class GalachainService { + static evaluateTransaction( + methodName: string, + contract: TGalachainContract, + dto: ChainCallDTO, + privateKey: string, + ) { + const methodMap = { + development: this.evaluateTransactionLocal.bind(this), + production: this.submitTransactonREST.bind(this), + }; + return methodMap[NODE_ENV](methodName, contract, dto, privateKey); + } + + static submitTransaction(methodName: string, contract: TGalachainContract, dto: ChainCallDTO, privateKey: string) { + const methodMap = { + development: this.submitTransactionLocal.bind(this), + production: this.submitTransactonREST.bind(this), + }; + return methodMap[NODE_ENV](methodName, contract, dto, privateKey); + } + + static async evaluateTransactionLocal( + methodName: string, + contract: TGalachainContract, + dto: ChainCallDTO, + privateKey: string, + ) { + const client = getClient(GalachainRole.Curator); // TODO Make this dynamic + const response = await client.forContract(contract).evaluateTransaction(methodName, dto.signed(privateKey)); + + if (GalaChainResponse.isError(response)) { + throw new Error(`${response.Message} (${response.ErrorKey})`); + } else { + return response.Data; + } + } + + static async submitTransactionLocal( + methodName: string, + contract: TGalachainContract, + dto: ChainCallDTO, + privateKey: string, + ) { + const client = getClient(GalachainRole.Curator); // TODO Make this dynamic + const response = await client.forContract(contract).submitTransaction(methodName, dto.signed(privateKey)); + + if (GalaChainResponse.isError(response)) { + throw new BadRequestError(`${response.Message} (${response.ErrorKey})`); + } else { + return response.Data; + } + } + + static async submitTransactonREST( + methodName: string, + contract: TGalachainContract, + dto: ChainCallDTO, + privateKey: string, + ) { + const signedDto = dto.signed(privateKey); + const url = new URL(GALACHAIN_URL); + url.pathname = `${url.pathname}/${contract.channelName}/${contract.chaincodeName}-${contract.contractName}/${methodName}`; + + try { + const res = await axios({ + method: 'POST', + url: url.toString(), + headers: {}, + data: instanceToPlain(signedDto), + }); + return res.data; + } catch (error) { + logger.error(error.response.data); + throw new BadRequestError(error.response.data.message); + } + } + + static getProfile(contract: TGalachainContract, privateKey: string) { + const dto = new GetMyProfileDto().signed(privateKey, false); + return this.evaluateTransaction('GetMyProfile', contract, dto, privateKey); + } + + static registerUser(contract: TGalachainContract, publicKey: string, privateKey: string) { + const dto = new RegisterUserDto(); + dto.publicKey = publicKey; + dto.sign(privateKey, false); + + return this.submitTransaction('RegisterEthUser', contract, dto, privateKey); + } + + static async balanceOf(contract: TGalachainContract, tokenClassKey: TGalachainToken, privateKey: string) { + const tokenClass = plainToInstance(TokenInstanceKey, tokenClassKey); + const owner = new Wallet(privateKey).address; + const dto = await createValidDTO(FetchBalancesDto, { + owner: identityKey(owner), + ...tokenClass, + }); + return this.evaluateTransaction('FetchBalances', contract, dto, privateKey); + } + + static async create( + contract: TGalachainContract, + tokenInfo: { + image: string; + name: string; + description: string; + symbol: string; + decimals: number; + maxSupply: any; + }, + tokenClassKey: TGalachainToken, + privateKey: string, + ) { + const tokenClass = plainToInstance(TokenClassKey, tokenClassKey); + const dto = await createValidDTO<CreateTokenClassDto>(CreateTokenClassDto, { + tokenClass, + ...tokenInfo, + }); + + return this.submitTransaction('CreateTokenClass', contract, dto, privateKey); + } + + static async mint( + contract: TGalachainContract, + tokenClassKey: TGalachainToken, + to: string, + amount: number, + privateKey: string, + ) { + const tokenClass = plainToInstance(TokenClassKey, tokenClassKey); + const dto = await createValidDTO<MintTokenDto>(MintTokenDto, { + owner: identityKey(to), + tokenClass, + quantity: new BigNumber(amount) as any, + }); + + return this.submitTransaction('MintToken', contract, dto, privateKey); + } + + static async approve( + contract: TGalachainContract, + tokenClassKey: TGalachainToken, + spender: string, + amount: number, + allowanceType: AllowanceType, + privateKey: string, + ) { + const dto = await createValidDTO<GrantAllowanceDto>(GrantAllowanceDto, { + tokenInstance: TokenInstanceKey.nftKey(tokenClassKey, TokenInstance.FUNGIBLE_TOKEN_INSTANCE).toQueryKey(), + allowanceType, + quantities: [{ user: identityKey(spender), quantity: new BigNumber(amount) as any }], + uses: new BigNumber(amount) as any, + }); + + return this.submitTransaction('GrantAllowance', contract, dto, privateKey); + } + + static async transfer( + contract: TGalachainContract, + tokenClassKey: TGalachainToken, + to: string, + amount: number, + instance: BigNumber, + privateKey: string, + ) { + const tokenInstance = plainToInstance(TokenInstanceKey, { ...tokenClassKey, instance }); + const dto = await createValidDTO(TransferTokenDto, { + from: identityKey(new Wallet(privateKey).address), + to: identityKey(to), + tokenInstance, + quantity: new BigNumber(amount) as any, + }); + + return this.submitTransaction('TransferToken', contract, dto, privateKey); + } +} diff --git a/apps/api/src/app/services/GitcoinService.ts b/apps/api/src/app/services/GitcoinService.ts new file mode 100644 index 000000000..721975b27 --- /dev/null +++ b/apps/api/src/app/services/GitcoinService.ts @@ -0,0 +1,32 @@ +import axios from 'axios'; +import { GITCOIN_API_KEY } from '../config/secrets'; +import { logger } from '../util/logger'; +export default class GitcoinService { + static async submitPassport(scorerId: number, address: string) { + await axios({ + method: 'POST', + url: 'https://api.scorer.gitcoin.co/registry/submit-passport', + headers: { 'X-API-KEY': GITCOIN_API_KEY }, + data: { + address, + scorer_id: scorerId, + }, + }); + } + + static async getScoreUniqueHumanity(scorerId: number, address: string) { + try { + await this.submitPassport(scorerId, address); + + const { data } = await axios({ + method: 'GET', + url: `https://api.scorer.gitcoin.co/registry/score/${scorerId}/${address}`, + headers: { 'X-API-KEY': GITCOIN_API_KEY }, + }); + return { score: data.score === '0E-9' ? 0 : data.score }; + } catch (error) { + logger.error(error.message); + return { error: `Could not get a score for ${address}.` }; + } + } +} diff --git a/apps/api/src/app/services/IPFSService.ts b/apps/api/src/app/services/IPFSService.ts new file mode 100644 index 000000000..be61d081d --- /dev/null +++ b/apps/api/src/app/services/IPFSService.ts @@ -0,0 +1,44 @@ +import { API_URL, NODE_ENV } from '../config/secrets'; +import { NFTVariant } from '@thxnetwork/common/enums'; +import { ERC721Document, ERC1155Document } from '@thxnetwork/api/models'; +import axios from 'axios'; +import pinataSDK from '@pinata/sdk'; +import https from 'https'; + +const pinata = new pinataSDK({ pinataJWTKey: process.env.PINATA_API_JWT }); + +if (NODE_ENV !== 'production') { + const httpsAgent = new https.Agent({ + rejectUnauthorized: false, + }); + axios.defaults.httpsAgent = httpsAgent; +} + +export async function addUrlSource(url: string) { + const response = await axios.get(url, { responseType: 'stream' }); + const urlParts = url.split('/'); + const name = urlParts[urlParts.length - 1]; + const { IpfsHash } = await pinata.pinFileToIPFS(response.data, { + pinataMetadata: { name }, + pinataOptions: { cidVersion: 0 }, + }); + return IpfsHash; +} + +async function getTokenURI(nft: ERC721Document | ERC1155Document, metadataId: string, tokenId?: string) { + const tokenUri = { + [NFTVariant.ERC721]: metadataId, + [NFTVariant.ERC1155]: tokenId, + }; + // During tests we can not grab data from an url due to TLS issues, hence we return the internally used tokenUri + if (NODE_ENV === 'test') return tokenUri[nft.variant]; + + const metadataUrl = { + [NFTVariant.ERC721]: `${API_URL}/v1/metadata/${metadataId}`, + [NFTVariant.ERC1155]: `${API_URL}/v1/metadata/erc1155/${nft._id}/${tokenId}`, + }; + + return await addUrlSource(metadataUrl[nft.variant]); +} + +export default { addUrlSource, getTokenURI }; diff --git a/apps/api/src/app/services/IdentityService.ts b/apps/api/src/app/services/IdentityService.ts new file mode 100644 index 000000000..95c22ee79 --- /dev/null +++ b/apps/api/src/app/services/IdentityService.ts @@ -0,0 +1,33 @@ +import { WalletVariant } from '@thxnetwork/common/enums'; +import { Wallet, Identity, PoolDocument } from '@thxnetwork/api/models'; +import { uuidV1 } from '../util/uuid'; + +export default class IdentityService { + static getUUID(pool: PoolDocument, salt: string) { + const poolId = String(pool._id); + return uuidV1(`${poolId}${salt}`); + } + + // Derive uuid v1 from poolId + salt. Using uuid v1 format so we can + // validate the input using express-validator + static getIdentityForSalt(pool: PoolDocument, salt: string) { + const uuid = this.getUUID(pool, salt); + return Identity.findOneAndUpdate( + { poolId: pool._id, uuid }, + { poolId: pool._id, uuid }, + { new: true, upsert: true }, + ); + } + + static async forceConnect(pool: PoolDocument, account: TAccount) { + // Search for WalletConnect wallets for this sub + const wallets = await Wallet.find({ sub: account.sub, variant: WalletVariant.WalletConnect }); + if (!wallets.length) return; + + // Create a list of uuids for these wallets + const uuids = wallets.map((wallet) => this.getUUID(pool, wallet.address)); + + // Find any identity for these uuids and update + await Identity.findOneAndUpdate({ uuid: { $in: uuids } }, { sub: account.sub }); + } +} diff --git a/apps/api/src/app/services/ImageService.ts b/apps/api/src/app/services/ImageService.ts new file mode 100644 index 000000000..137330c27 --- /dev/null +++ b/apps/api/src/app/services/ImageService.ts @@ -0,0 +1,29 @@ +import short from 'short-uuid'; +import { AWS_S3_PUBLIC_BUCKET_NAME, AWS_S3_PUBLIC_BUCKET_REGION } from '@thxnetwork/api/config/secrets'; +import { s3Client } from '@thxnetwork/api/util/s3'; +import { PutObjectCommand } from '@aws-sdk/client-s3'; + +async function upload(file: Express.Multer.File) { + const [originalname, extension] = file.originalname.split('.'); + const filename = + originalname.toLowerCase().split(' ').join('-').split('.') + '-' + short.generate() + `.${extension}`; + const type = extension === 'svg' ? 'image/svg+xml' : 'image/*'; + return this.uploadToS3(file.buffer, filename, type); +} + +async function uploadToS3(fileBuffer: Buffer, filename, type) { + await s3Client.send( + new PutObjectCommand({ + Key: filename, + Bucket: AWS_S3_PUBLIC_BUCKET_NAME, + ACL: 'public-read', + Body: fileBuffer, + ContentType: type, + ContentDisposition: 'inline', + }), + ); + + return `https://${AWS_S3_PUBLIC_BUCKET_NAME}.s3.${AWS_S3_PUBLIC_BUCKET_REGION}.amazonaws.com/${filename}`; +} + +export default { upload, uploadToS3 }; diff --git a/apps/api/src/app/services/InvoiceService.ts b/apps/api/src/app/services/InvoiceService.ts new file mode 100644 index 000000000..7e1f78297 --- /dev/null +++ b/apps/api/src/app/services/InvoiceService.ts @@ -0,0 +1,141 @@ +import { planPricingMap } from '@thxnetwork/common/constants'; +import { + Pool, + Invoice, + QuestDailyEntry, + QuestInviteEntry, + QuestSocialEntry, + QuestCustomEntry, + QuestWeb3Entry, + QuestGitcoinEntry, +} from '../models'; +import AccountProxy from '../proxies/AccountProxy'; +import { logger } from '../util/logger'; +import { AccountPlanType } from '@thxnetwork/common/enums'; +import { startOfMonth, endOfMonth } from 'date-fns'; + +export default class InvoiceService { + /** + * Upsert invoices for the current month. Periodically (daily) invoked by the agenda job scheduler. + */ + static async upsertJob() { + const currentDate = new Date(); + // Define the start and end dates for the month range + const invoicePeriodstartDate = startOfMonth(currentDate); + const invoicePeriodEndDate = endOfMonth(currentDate); + + await this.upsertInvoices(invoicePeriodstartDate, invoicePeriodEndDate); + } + + /** + * Upsert invoices for a given period. Used independently for testing and backfills. + * @param invoicePeriodstartDate + * @param invoicePeriodEndDate + */ + static async upsertInvoices(invoicePeriodstartDate: Date, invoicePeriodEndDate: Date) { + // Determine the lookup stages for the quest entries in the pools pipeline + const questEntryModels = [ + QuestDailyEntry, + QuestInviteEntry, + QuestSocialEntry, + QuestCustomEntry, + QuestWeb3Entry, + QuestGitcoinEntry, + ]; + + // Get all relevant pools + const pools = await Pool.find({ 'settings.isPublished': true }); + const questEntriesByCampaign = await Promise.all( + pools.map(async (pool) => { + const uniqueEntriesByVariant = await Promise.all( + questEntryModels.map(async (model) => { + return await model + .countDocuments({ + poolId: pool.id, + createdAt: { $gte: invoicePeriodstartDate, $lte: invoicePeriodEndDate }, + }) + .distinct('sub'); + }), + ); + const flattenedArray = uniqueEntriesByVariant.flat(); + + return { poolId: pool.id, poolSub: pool.sub, mapCount: new Set(flattenedArray).size }; + }), + ); + + // Get the pool owner accounts to send the invoices + const subs = questEntriesByCampaign.map(({ poolSub }) => poolSub); + const accounts = await AccountProxy.find({ subs }); + + // Build operations array for the current month metrics + const operations = questEntriesByCampaign.map(({ poolId, poolSub, mapCount }) => { + try { + const account = accounts.find((a) => a.sub === poolSub); + // If the account can not be found, has no email or plan then notify admin. + // Continue with invoice generation for future reference + // @todo: notify admin + if (!account) { + logger.info(`Account ${account.sub} not found for invoicing.`); + } + if (!account.email) { + logger.info(`Account ${account.sub} has no email for invoicing.`); + } + if (![AccountPlanType.Lite, AccountPlanType.Premium].includes(account.plan)) { + logger.info(`Account ${account.sub} has no plan for invoicing.`); + } + + return { + updateOne: { + filter: { + poolId, + periodStartDate: invoicePeriodstartDate, + periodEndDate: invoicePeriodEndDate, + }, + update: { + $set: { + poolId, + periodStartDate: invoicePeriodstartDate, + periodEndDate: invoicePeriodEndDate, + mapCount, + mapLimit: planPricingMap[account.plan].subscriptionLimit, + ...this.createInvoiceDetails(account, mapCount), + }, + }, + upsert: true, + }, + }; + } catch (error) { + logger.error(error); + } + }); + + // Remove empty ops and bulk write the invoices + await Invoice.bulkWrite(operations.filter((op) => !!op)); + } + + /** + * Create invoice details for the given account and monthly active participant count + * @param account + * @param mapCount + * @returns invoice details used for upsert in db + */ + static createInvoiceDetails(account: TAccount, mapCount: number) { + const countAdditionalUnits = (mapCount: number, limit: number) => { + return Math.max(0, mapCount - limit); + }; + const plan = account.plan || AccountPlanType.Lite; + const { subscriptionLimit, costPerUnit, costSubscription } = planPricingMap[plan]; + + // Plan limit is subtracted from unit count as costs are included in subscription costs + const additionalUnitCount = countAdditionalUnits(mapCount, subscriptionLimit); + + return { + additionalUnitCount, + costPerUnit, + costSubscription, + costTotal: costSubscription + additionalUnitCount * costPerUnit, + currency: 'USDC', + plan: account.plan, + }; + } +} diff --git a/apps/api/src/app/services/LiquidityService.ts b/apps/api/src/app/services/LiquidityService.ts new file mode 100644 index 000000000..704099d96 --- /dev/null +++ b/apps/api/src/app/services/LiquidityService.ts @@ -0,0 +1,23 @@ +import { contractNetworks, getArtifact } from '@thxnetwork/api/hardhat'; +import { WalletDocument } from '../models/Wallet'; +import { getProvider } from '../util/network'; +import TransactionService from './TransactionService'; +import BalancerService from './BalancerService'; + +export default class LiquidityService { + static async create(wallet: WalletDocument, usdcAmountInWei: string, thxAmountInWei: string, slippage: string) { + const { to, data } = await BalancerService.buildJoin(wallet, usdcAmountInWei, thxAmountInWei, slippage); + return await TransactionService.proposeSafeAsync(wallet, to, data); + } + + static async stake(wallet: WalletDocument, amountInWei: string) { + const { web3 } = getProvider(wallet.chainId); + + // Deposit the BPT into the gauge + const bptGauge = new web3.eth.Contract(getArtifact('BPTGauge').abi, contractNetworks[wallet.chainId].BPTGauge); + const fn = bptGauge.methods.deposit(amountInWei); + + // Propose tx data to relayer and return safeTxHash to client to sign + return await TransactionService.sendSafeAsync(wallet, bptGauge.options.address, fn); + } +} diff --git a/apps/api/src/app/services/LockService.ts b/apps/api/src/app/services/LockService.ts new file mode 100644 index 000000000..951387b6f --- /dev/null +++ b/apps/api/src/app/services/LockService.ts @@ -0,0 +1,50 @@ +import { QuestSocialDocument } from '../models'; +import QuestService from './QuestService'; +import { serviceMap } from './interfaces/IQuestService'; + +async function getIsUnlocked(lock: TQuestLock, account: TAccount): Promise<boolean> { + const ids: any = [{ sub: account.sub }]; + + // For these social quests we also search for existing entries by platformUserId + const quest = (await QuestService.findById(lock.variant, lock.questId)) as QuestSocialDocument; + if (quest.interaction) { + const platformUserId = QuestService.findUserIdForInteraction(account, quest.interaction); + if (platformUserId) ids.push({ platformUserId }); + } + + const Entry = serviceMap[lock.variant].models.entry; + const exists = await Entry.exists({ questId: lock.questId, $or: ids }); + + return !!exists; +} + +async function getIsLocked(locks: TQuestLock[], account: TAccount) { + if (!locks.length || !account) return false; + + // Check if there are entries for the remaining quests + const promises = locks.map((lock) => getIsUnlocked(lock, account)); + const results = await Promise.allSettled(promises); + const anyRejected = results.some((result) => result.status === 'rejected'); + if (anyRejected) return true; + + return results + .filter((result) => result.status === 'fulfilled') + .map((result: any & { value: boolean }) => result.value) + .includes(false); +} + +async function removeAllLocks(questId: string) { + for (const variant in Object.keys(serviceMap)) { + const Quest = serviceMap[variant].models.quest; + const lockedQuests = await Quest.find({ 'locks.questId': questId }); + + for (const lockedQuest of lockedQuests) { + const index = lockedQuest.locks.findIndex((lock: TQuestLock) => lock.questId === questId); + const locks = lockedQuest.locks.splice(index, 1); + + await lockedQuest.updateOne({ locks }); + } + } +} + +export default { getIsLocked, removeAllLocks }; diff --git a/apps/api/src/app/services/MailService.ts b/apps/api/src/app/services/MailService.ts new file mode 100644 index 000000000..0aafd427e --- /dev/null +++ b/apps/api/src/app/services/MailService.ts @@ -0,0 +1,33 @@ +import { + AUTH_URL, + NODE_ENV, + CYPRESS_EMAIL, + AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY, +} from '@thxnetwork/api/config/secrets'; +import path from 'path'; +import { assetsPath } from '../util/path'; +import ejs from 'ejs'; +import { sendMail } from '@thxnetwork/common/mail'; +import { logger } from '../util/logger'; + +const mailTemplatePath = path.join(assetsPath, 'views', 'email'); + +const send = async (to: string, subject: string, htmlContent: string, link = { src: '', text: '' }) => { + if (!to) return; + + const html = await ejs.renderFile( + path.join(mailTemplatePath, 'base-template.ejs'), + { link, subject, htmlContent, baseUrl: AUTH_URL }, + { async: true }, + ); + + if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY || NODE_ENV === 'test' || CYPRESS_EMAIL === to) { + logger.debug({ message: 'Not sending e-mail', link }); + return; + } + + return sendMail(to, subject, html); +}; + +export default { send }; diff --git a/apps/api/src/app/services/NotificationService.ts b/apps/api/src/app/services/NotificationService.ts new file mode 100644 index 000000000..782794e62 --- /dev/null +++ b/apps/api/src/app/services/NotificationService.ts @@ -0,0 +1,142 @@ +import { QuestVariant } from '@thxnetwork/common/enums'; +import { PoolDocument } from '@thxnetwork/api/models'; +import { logger } from '../util/logger'; +import { sleep } from '../util'; +import { Notification, Widget, Participant } from '@thxnetwork/api/models'; +import { DiscordButtonVariant } from '../events/InteractionCreated'; +import { ButtonStyle } from 'discord.js'; +import { WIDGET_URL } from '../config/secrets'; +import { celebratoryWords } from '../util/dictionaries'; +import AccountProxy from '../proxies/AccountProxy'; +import MailService from './MailService'; +import PoolService from './PoolService'; +import BrandService from './BrandService'; +import DiscordDataProxy from '../proxies/DiscordDataProxy'; + +const MAIL_CHUNK_SIZE = 600; + +async function send( + pool: PoolDocument, + { subjectId, subject, message, link }: Partial<TNotification> & { link?: { src: string; text: string } }, +) { + const participants = await Participant.find({ poolId: pool._id, isSubscribed: true }); + const subs = participants.map((p) => p.sub); + const accounts = (await AccountProxy.find({ subs })).filter((a) => a.email); + + // Create chunks for bulk email sending to avoid hitting Sendgrit rate limits + for (let i = 0; i < subs.length; i += MAIL_CHUNK_SIZE) { + const chunk = subs.slice(i, i + MAIL_CHUNK_SIZE); + await Promise.all( + chunk.map(async (sub) => { + try { + // Make sure to not sent duplicate notifications + // for the same subjectId + const isNotifiedAlready = await Notification.exists({ sub, subjectId }); + if (isNotifiedAlready) return; + + const account = accounts.find((a) => a.sub === sub); + await MailService.send(account.email, subject, message, link); + + await Notification.create({ sub, poolId: pool._id, subjectId, subject, message }); + } catch (error) { + logger.error(error); + } + }), + ); + + // Sleep 60 seconds before sending the next chunk + await sleep(60); + } +} + +async function notify(variant: QuestVariant, quest: TQuest) { + const [pool, brand, widget] = await Promise.all([ + PoolService.getById(quest.poolId), + BrandService.get(quest.poolId), + Widget.findOne({ poolId: quest.poolId }), + ]); + + sendQuestPublishEmail(pool, variant, quest as TQuest, widget); + sendQuestPublishNotification(pool, variant, quest as TQuest, widget, brand); +} + +async function sendQuestPublishEmail(pool: PoolDocument, variant: QuestVariant, quest: TQuest, widget: TWidget) { + const { amount, amounts } = quest as any; + const subject = `🎁 New ${QuestVariant[variant]} Quest: Earn ${amount || amounts[0]} pts!"`; + const message = `<p style="font-size: 18px">Earn ${amount || amounts[0]} points!🔔</p> + <p>Hi! <strong>${pool.settings.title}</strong> just published a new ${QuestVariant[variant]} Quest. + <p><strong>${quest.title}</strong><br />${quest.description}.</p>`; + const src = WIDGET_URL + `/c/${pool.settings.slug}`; + + send(pool, { + subjectId: quest.uuid, + subject, + message, + link: { text: `Complete ${QuestVariant[variant]} Quest`, src }, + }); +} + +async function sendQuestPublishNotification( + pool: PoolDocument, + variant: QuestVariant, + quest: TQuest, + widget: TWidget, + brand?: TBrand, +) { + const theme = JSON.parse(widget.theme); + const { amount, amounts } = quest as any; + + const embed = { + title: quest.title, + description: quest.description, + author: { + name: pool.settings.title, + icon_url: brand ? brand.logoImgUrl : '', + url: widget.domain, + }, + image: { url: quest.image }, + color: parseInt(theme.elements.btnBg.color.replace(/^#/, ''), 16), + fields: [ + { + name: 'Points', + value: `${amount || amounts[0]}`, + inline: true, + }, + { + name: 'Type', + value: `${QuestVariant[quest.variant]}`, + inline: true, + }, + ], + }; + + await DiscordDataProxy.sendChannelMessage( + pool, + `Hi @everyone! We published a **${QuestVariant[variant]} Quest**.`, + [embed], + [ + { + customId: `${DiscordButtonVariant.QuestComplete}:${quest.variant}:${quest._id}`, + label: 'Complete Quest!', + style: ButtonStyle.Success, + }, + { label: 'More Info', style: ButtonStyle.Link, url: WIDGET_URL + `/c/${pool.settings.slug}` }, + ], + ); +} + +async function sendQuestEntryNotification(pool: PoolDocument, quest: TQuest, account: TAccount, amount: number) { + const index = Math.floor(Math.random() * celebratoryWords.length); + const discord = account.tokens && account.tokens.find((a) => a.kind === 'discord'); + const user = discord && discord.userId ? `<@${discord.userId}>` : `**${account.username}**`; + const button = { + customId: `${DiscordButtonVariant.QuestComplete}:${quest.variant}:${quest._id}`, + label: 'Complete Quest', + style: ButtonStyle.Primary, + }; + const content = `${celebratoryWords[index]} ${user} completed the **${quest.title}** quest and earned **${amount} points.**`; + + await DiscordDataProxy.sendChannelMessage(pool, content, [], [button]); +} + +export default { send, notify, sendQuestEntryNotification }; diff --git a/apps/api/src/app/services/ParticipantService.ts b/apps/api/src/app/services/ParticipantService.ts new file mode 100644 index 000000000..34973b370 --- /dev/null +++ b/apps/api/src/app/services/ParticipantService.ts @@ -0,0 +1,60 @@ +import { Document } from 'mongoose'; +import { DiscordReaction, Participant, TwitterUser } from '../models'; +import ReCaptchaService from '@thxnetwork/api/services/ReCaptchaService'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import DiscordService from './DiscordService'; +import { DiscordUser } from '../models/DiscordUser'; + +export default class ParticipantService { + static async decorate( + data: Document & (TQuestEntry | TRewardPayment), + { accounts, participants }: { accounts: TAccount[]; participants: TParticipant[] }, + ) { + const account = accounts.find((a) => a.sub === data.sub); + const pointBalance = participants.find((p) => account.sub === String(p.sub)); + const tokens = await Promise.all( + account.tokens.map(async (token: TToken) => { + if (token.kind !== 'twitter') return token; + const user = await TwitterUser.findOne({ userId: token.userId }); + return { ...token, user }; + }), + ); + + return { + ...data.toJSON(), + account: { ...account, tokens }, + pointBalance: pointBalance ? pointBalance.balance : 0, + }; + } + + static async updateRiskScore( + account: TAccount, + poolId: string, + { token, recaptchaAction }: { token: string; recaptchaAction: string }, + ) { + // Get risk score from Google + const riskAnalysis = await ReCaptchaService.getRiskAnalysis({ + token, + recaptchaAction, + }); + + // Update the participant's risk score + return await Participant.findOneAndUpdate({ sub: account.sub, poolId }, { riskAnalysis }, { new: true }); + } + + static async findUser(token: TToken, { userId, guildId }: { userId: string; guildId?: string }) { + const userModelMap = { + [AccessTokenKind.Twitter]: () => TwitterUser.findOne({ userId }), + [AccessTokenKind.Discord]: () => DiscordUser.findOne({ userId, guildId }), + }; + + const user = userModelMap[token.kind] && (await userModelMap[token.kind]()); + + return { + kind: token.kind, + userId: token.userId, + metadata: token.metadata, + user, + } as unknown as TToken; + } +} diff --git a/apps/api/src/app/services/PaymentService.ts b/apps/api/src/app/services/PaymentService.ts new file mode 100644 index 000000000..bb74d1a51 --- /dev/null +++ b/apps/api/src/app/services/PaymentService.ts @@ -0,0 +1,112 @@ +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { Pool, WalletDocument } from '../models'; +import { getProvider } from '../util/network'; +import { BigNumber } from 'alchemy-sdk'; +import { differenceInSeconds, isBefore, subWeeks } from 'date-fns'; +import { AccountPlanType, ChainId } from '@thxnetwork/common/enums'; +import { Payment } from '../models/Payment'; +import { planPricingMap } from '@thxnetwork/common/constants'; +import { parseUnits } from 'ethers/lib/utils'; +import ContractService from './ContractService'; +import TransactionService from './TransactionService'; +import SafeService from './SafeService'; +import BalancerService from './BalancerService'; + +const ONE_DAY = 60 * 60 * 24; + +export default class PaymentService { + static async deposit(safe: WalletDocument, sub: string, amountInWei: BigNumber) { + const { web3 } = getProvider(safe.chainId); + const addresses = contractNetworks[safe.chainId]; + const contract = new web3.eth.Contract( + contractArtifacts['THXPaymentSplitter'].abi, + addresses.THXPaymentSplitter, + ); + + // @dev Using default slippage value here as payments + const { minBPTOut } = await BalancerService.buildJoin(safe, amountInWei.toString(), '0', '50'); + const fn = contract.methods.deposit(safe.address, amountInWei, minBPTOut); + + await Payment.create({ poolId: safe.poolId, sub, amountInWei }); + + return await TransactionService.sendSafeAsync(safe, addresses.THXPaymentSplitter, fn); + } + + static async balanceOf(wallet: WalletDocument) { + // TODO Deploy Polygon PaymentSplitter before using this middleware + const { THXPaymentSplitter } = contractNetworks[wallet.chainId]; + if (!THXPaymentSplitter && wallet.chainId === ChainId.Polygon) { + return '0'; + } + + const splitter = ContractService.getContract('THXPaymentSplitter', wallet.chainId, THXPaymentSplitter); + const balance = await splitter.balanceOf(wallet.address); + return balance.toString(); + } + + static async getRate(wallet: WalletDocument) { + const splitter = ContractService.getContract( + 'THXPaymentSplitter', + wallet.chainId, + contractNetworks[wallet.chainId].THXPaymentSplitter, + ); + const rate = await splitter.rates(wallet.address); + return rate.toString(); + } + + static async setRate(safe: WalletDocument, plan: AccountPlanType) { + const { web3 } = getProvider(safe.chainId); + const addresses = contractNetworks[safe.chainId]; + const contract = new web3.eth.Contract( + contractArtifacts['THXPaymentSplitter'].abi, + addresses.THXPaymentSplitter, + ); + // Convert plan pricing to rate in wei per second + const pricing = planPricingMap[plan]; + // Using 6 decimals for USDC + const costInWeiPerThirtyDays = BigNumber.from(parseUnits(pricing.costSubscription.toString(), 6)); + // Plan pricing is determined on a per 4 week basis + const rateInWeiPerSecond = costInWeiPerThirtyDays.div(4 * 7 * 24 * 60 * 60); + const fn = contract.methods.setRate(rateInWeiPerSecond); + + return await TransactionService.sendSafeAsync(safe, addresses.PaymentSplitter, fn); + } + + static async getTimeLeftInSeconds(safe: WalletDocument, pool: TPool) { + const now = new Date(); + const isTrial = isBefore(pool.trialEndsAt, now); + if (isTrial) return BigNumber.from(differenceInSeconds(pool.trialEndsAt, now)); + + // Devide balance by rate and calculate time left for the pool + const balanceInWei = await this.balanceOf(safe); + const rateInWeiPerSecond = await this.getRate(safe); + return BigNumber.from(balanceInWei).div(rateInWeiPerSecond); + } + + static async assertPaymentsJob() { + // Skip pools that have no trialEnd date (legacy) or are still in the first week of their trial + const pools = await Pool.find({ trialEndsAt: { $exists: true, $lt: subWeeks(new Date(), 1) } }); + for (const pool of pools) { + // Get campaing safe + const safe = await SafeService.findOneByPool(pool); + const timeLeftInSeconds = await this.getTimeLeftInSeconds(safe, pool); + + // Insufficient payments + if (timeLeftInSeconds.eq(0)) { + // Send a reminder to make payment and inform about campaign pause change + } + // 1 day before balance hitting zero send a reminder to make payment + else if (timeLeftInSeconds.lt(ONE_DAY)) { + // Send reminder to make payment + } + // 3 days before balance hitting zero send a reminder to make payment + else if (timeLeftInSeconds.lt(ONE_DAY * 3)) { + // Send reminder to make payment + } + // 1 week before balance hitting zero send a reminder to make payment + else if (timeLeftInSeconds.lt(ONE_DAY * 7)) { + // Send reminder to make payment + } + } + } +} diff --git a/apps/api/src/app/services/PointBalanceService.ts b/apps/api/src/app/services/PointBalanceService.ts new file mode 100644 index 000000000..3463cc14e --- /dev/null +++ b/apps/api/src/app/services/PointBalanceService.ts @@ -0,0 +1,29 @@ +import { Participant, PoolDocument } from '@thxnetwork/api/models'; + +async function add(pool: PoolDocument, account: TAccount, amount: number) { + const participant = await Participant.findOne({ poolId: pool._id, sub: account.sub }); + const balance = participant ? Number(participant.balance) + Number(amount) : Number(amount); + + await Participant.updateOne( + { poolId: String(pool._id), sub: account.sub }, + { poolId: String(pool._id), sub: account.sub, balance }, + { upsert: true }, + ); +} + +async function subtract(pool: PoolDocument, account: TAccount, price: number) { + if (!price) return; + + const participant = await Participant.findOne({ poolId: pool._id, sub: account.sub }); + if (!participant) return; + + const balance = Number(participant.balance) >= price ? Number(participant.balance) - price : 0; + + await Participant.updateOne( + { poolId: String(pool._id), sub: account.sub }, + { poolId: String(pool._id), sub: account.sub, balance }, + { upsert: true }, + ); +} + +export default { add, subtract }; diff --git a/apps/api/src/app/services/PoolService.ts b/apps/api/src/app/services/PoolService.ts new file mode 100644 index 000000000..9d63ce88c --- /dev/null +++ b/apps/api/src/app/services/PoolService.ts @@ -0,0 +1,406 @@ +import { AccessTokenKind, ChainId, CollaboratorInviteState, OAuthDiscordScope } from '@thxnetwork/common/enums'; +import { v4 } from 'uuid'; +import { AccountVariant } from '@thxnetwork/common/enums'; +import { DASHBOARD_URL } from '../config/secrets'; +import { DEFAULT_COLORS, DEFAULT_ELEMENTS } from '@thxnetwork/common/constants'; +import { logger } from '../util/logger'; +import { getsigningSecret } from '../util/signingsecret'; +import { + Pool, + PoolDocument, + RewardCoin, + RewardNFT, + Collaborator, + CollaboratorDocument, + Client, + DiscordGuild, + Identity, + Participant, + QuestInvite, + QuestWeb3, + QuestCustom, + RewardCustom, + Widget, + QuestSocial, + QuestDaily, + CouponCode, + WalletDocument, +} from '@thxnetwork/api/models'; + +import AccountProxy from '../proxies/AccountProxy'; +import DiscordDataProxy from '../proxies/DiscordDataProxy'; +import MailService from './MailService'; +import SafeService from './SafeService'; +import ParticipantService from './ParticipantService'; +import DiscordService from './DiscordService'; +import ContractService from './ContractService'; + +export const ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; + +async function isAudienceAllowed(aud: string, poolId: string) { + return !!(await Client.exists({ clientId: aud, poolId })); +} + +async function isSubjectAllowed(sub: string, poolId: string) { + const isOwner = await Pool.exists({ + _id: poolId, + sub, + }); + const isCollaborator = await Collaborator.exists({ sub, poolId, state: CollaboratorInviteState.Accepted }); + return isOwner || isCollaborator; +} + +async function getById(id: string) { + const pool = await Pool.findById(id); + const safe = await SafeService.findOneByPool(pool, pool.chainId); + pool.safe = safe; + return pool; +} + +function getByAddress(address: string) { + return Pool.findOne({ address }); +} + +async function deploy(sub: string, title: string): Promise<PoolDocument> { + const pool = await Pool.create({ + sub, + token: v4(), + signingSecret: getsigningSecret(64), + settings: { + title, + description: '', + isArchived: false, + isWeeklyDigestEnabled: true, + isTwitterSyncEnabled: false, + defaults: { + conditionalRewards: { title: '', description: '', amount: 50 }, + }, + authenticationMethods: [ + AccountVariant.EmailPassword, + AccountVariant.Metamask, + AccountVariant.SSOGoogle, + AccountVariant.SSODiscord, + ], + }, + }); + + await Widget.create({ + uuid: v4(), + poolId: pool._id, + align: 'right', + message: 'Hi there!👋 Click me to complete quests and earn rewards...', + domain: 'https://www.example.com', + theme: JSON.stringify({ elements: DEFAULT_ELEMENTS, colors: DEFAULT_COLORS }), + }); + + return Pool.findByIdAndUpdate(pool._id, { 'settings.slug': String(pool._id) }, { new: true }); +} + +async function getAllBySub(sub: string): Promise<PoolDocument[]> { + let pools = await Pool.find({ sub }); + + // Only query for collabs of not already owned pools + const collaborations = await Collaborator.find({ sub, poolId: { $nin: pools.map(({ _id }) => String(_id)) } }); + const poolIds = collaborations.map((c) => c.poolId); + if (poolIds.length) { + const collaborationPools = await Pool.find({ _id: poolIds }); + pools = pools.concat(collaborationPools); + } + + // Add Safes to pools + return await Promise.all( + pools.map(async (pool) => { + const safe = await SafeService.findOneByPool(pool); + return { ...pool.toJSON(), safe }; + }), + ); +} + +function getAll() { + return Pool.find({}); +} + +async function balanceOf(safe: WalletDocument) { + const contract = ContractService.getContract('USDC', safe.chainId, safe.address); + return await contract.balanceOf(safe.address); +} + +async function countByNetwork(chainId: ChainId) { + return Pool.countDocuments({ chainId }); +} + +async function find(model: any, pool: PoolDocument) { + return await model.find({ poolId: String(pool._id) }); +} + +async function findOwner(pool: PoolDocument) { + const account = await AccountProxy.findById(pool.sub); + account.tokens = account.tokens.map(({ kind, expiry, scopes }) => ({ kind, expiry, scopes } as TToken)); + return account; +} + +async function getQuestCount(pool: PoolDocument) { + const result = await Promise.all( + [QuestDaily, QuestInvite, QuestSocial, QuestCustom, QuestWeb3].map(async (model) => await find(model, pool)), + ); + return Array.from(new Set(result.flat(1))); +} + +async function getRewardCount(pool: PoolDocument) { + const result = await Promise.all( + [RewardCoin, RewardNFT, RewardCustom].map(async (model) => await find(model, pool)), + ); + return Array.from(new Set(result.flat(1))); +} + +async function findIdentities(pool: PoolDocument, page: number, limit: number) { + const startIndex = (page - 1) * limit; + const endIndex = page * limit; + const total = await Identity.find({ poolId: pool._id }).countDocuments().exec(); + + const identities = { + previous: startIndex > 0 && { + page: page - 1, + }, + next: endIndex < total && { + page: page + 1, + }, + limit, + total, + results: await Identity.aggregate([ + { $match: { poolId: String(pool._id) } }, + { $skip: startIndex }, + { $limit: limit }, + ]).exec(), + }; + + const subs = identities.results.filter(({ sub }) => !!sub).map(({ sub }) => sub); + const accounts = await AccountProxy.find({ subs }); + + identities.results = identities.results.map((identity: TIdentity) => ({ + ...identity, + account: accounts.find(({ sub }) => sub === identity.sub), + })); + + return identities; +} + +async function findCouponCodes( + { couponRewardId, query }: { couponRewardId: string; query: string }, + page: number, + limit: number, +) { + const startIndex = (page - 1) * limit; + const endIndex = page * limit; + const $match = { couponRewardId }; + if (query && query.length > 1) { + $match['code'] = { $regex: query, $options: 'i' }; + } + + const total = await CouponCode.find($match).countDocuments(); + const results = await CouponCode.aggregate([{ $match }, { $skip: startIndex }, { $limit: limit }]).exec(); + // Get subs for results + const subs = results.map(({ sub }) => sub); + // Get accounts for subs + const accounts = await AccountProxy.find({ subs }); + + return { + previous: startIndex > 0 && { + page: page - 1, + }, + next: endIndex < total && { + page: page + 1, + }, + total, + results: results.map((result) => { + const account = accounts.find(({ sub }) => sub === result.sub); + result.account = account; + return result; + }), + }; +} + +async function findParticipants(pool: PoolDocument, page: number, limit: number, query = '') { + const startIndex = (page - 1) * limit; + const endIndex = page * limit; + const poolId = String(pool._id); + const total = await Participant.countDocuments({ poolId }); + const $match = { poolId }; + + let accounts = [], + subs = []; + + // If a query is provided first get the accounts and subs based on the query + if (query) { + accounts = await AccountProxy.find({ query }); + subs = accounts.map((a) => a.sub); + $match['sub'] = { $in: subs }; + } + + const results = await Participant.aggregate([ + { $match }, + { + $addFields: { + rankSort: { + $cond: { + if: { $gt: ['$rank', 0] }, + then: '$rank', + else: Number.MAX_SAFE_INTEGER, + }, + }, + }, + }, + { $sort: { rankSort: 1 } }, + { $skip: startIndex }, + { $limit: limit }, + ]).exec(); + + // If a query was provided dont get accounts and subs based on participants + if (!query) { + subs = results.map((p) => p.sub); + accounts = await AccountProxy.find({ subs }); + } + + // Format the output + const participants = { + previous: startIndex > 0 && { + page: page - 1, + }, + next: endIndex < total && { + page: page + 1, + }, + total, + results, + }; + + const guild = await DiscordService.getGuild(poolId); + + participants.results = await Promise.all( + participants.results.map(async (participant) => { + let account: TAccount; + + try { + account = accounts.find((a) => a.sub === participant.sub); + account.tokens = await Promise.all( + account.tokens.map(async (token: TToken) => + ParticipantService.findUser(token, { userId: token.userId, guildId: guild && guild.id }), + ), + ); + } catch (error) { + logger.error(error); + } + + return { + ...participant, + account: account && { + email: account.email, + username: account.username, + profileImg: account.profileImg, + variant: account.variant, + tokens: account.tokens, + }, + }; + }), + ); + + return participants; +} + +async function getParticipantCount(pool: PoolDocument) { + return await Participant.countDocuments({ poolId: pool._id }); +} + +async function inviteCollaborator(pool: PoolDocument, email: string) { + const uuid = v4(); + let collaborator = await Collaborator.findOne({ email, poolId: pool._id }); + + if (collaborator) { + collaborator = await Collaborator.findByIdAndUpdate(collaborator._id, { uuid }, { new: true }); + } else { + collaborator = await Collaborator.create({ + email, + uuid, + poolId: pool._id, + state: CollaboratorInviteState.Pending, + }); + } + + const url = new URL(DASHBOARD_URL); + url.pathname = 'collaborator'; + url.searchParams.append('poolId', pool._id); + url.searchParams.append('collaboratorRequestToken', collaborator.uuid); + + await MailService.send( + email, + `👋 Collaboration Request: ${pool.settings.title}`, + `<p>Hi!👋</p><p>You have received a collaboration request for Quest & Reward campaign: <strong>${pool.settings.title}</strong></p>`, + { src: url.href, text: 'Accept Request' }, + ); + + return collaborator; +} + +async function getAccountGuilds(account: TAccount) { + // Try as this is potentially rate limited due to subsequent GET pool for id requests + try { + const token = await AccountProxy.getToken(account, AccessTokenKind.Discord, [ + OAuthDiscordScope.Identify, + OAuthDiscordScope.Guilds, + ]); + return DiscordDataProxy.getGuilds(token); + } catch (error) { + return []; + } +} + +async function findGuilds(pool: PoolDocument) { + const account = await AccountProxy.findById(pool.sub); + const userGuilds = await getAccountGuilds(account); + const guilds = await DiscordGuild.find({ poolId: pool._id }); + const promises = userGuilds.map(async (userGuild: { id: string; name: string }) => { + const guild = guilds.find(({ guildId }) => guildId === userGuild.id); + return await DiscordDataProxy.getGuild({ + ...(guild && guild.toJSON()), + ...userGuild, + guildId: userGuild.id, + poolId: pool._id, + isConnected: !!guild, + }); + }); + + return await Promise.all(promises); +} + +async function findCollaborators(pool: PoolDocument) { + const collabs = await Collaborator.find({ poolId: pool._id }); + const promises = collabs.map(async (collaborator: CollaboratorDocument) => { + if (collaborator.sub) { + const account = await AccountProxy.findById(collaborator.sub); + return { ...collaborator.toJSON(), account }; + } + return collaborator; + }); + return await Promise.all(promises); +} + +export default { + isAudienceAllowed, + isSubjectAllowed, + getById, + getByAddress, + deploy, + balanceOf, + getAllBySub, + getAll, + countByNetwork, + getParticipantCount, + getQuestCount, + getRewardCount, + findOwner, + findIdentities, + findParticipants, + findGuilds, + findCollaborators, + findCouponCodes, + inviteCollaborator, +}; diff --git a/apps/api/src/app/services/QuestCustomService.ts b/apps/api/src/app/services/QuestCustomService.ts new file mode 100644 index 000000000..cc54a5ca4 --- /dev/null +++ b/apps/api/src/app/services/QuestCustomService.ts @@ -0,0 +1,126 @@ +import { + QuestCustom, + QuestCustomDocument, + QuestCustomEntry, + Identity, + IdentityDocument, + Event, + WalletDocument, +} from '@thxnetwork/api/models'; +import { IQuestService } from './interfaces/IQuestService'; + +export default class QuestCustomService implements IQuestService { + models = { + quest: QuestCustom, + entry: QuestCustomEntry, + }; + + async findEntryMetadata({ quest }: { quest: QuestCustomDocument }) { + const uniqueParticipantIds = await QuestCustomEntry.countDocuments({ + questId: String(quest._id), + }).distinct('sub'); + + return { participantCount: uniqueParticipantIds.length }; + } + + async isAvailable({ + quest, + account, + }: { + quest: QuestCustomDocument; + wallet?: WalletDocument; + account?: TAccount; + data: Partial<TQuestCustomEntry>; + }): Promise<TValidationResult> { + const entries = await this.findAllEntries({ quest, account }); + if (quest.limit && entries.length >= quest.limit) { + return { result: false, reason: 'Quest entry limit has been reached.' }; + } + + return { result: true, reason: '' }; + } + + async getAmount({ quest }: { quest: QuestCustomDocument; wallet: WalletDocument; account: TAccount }) { + return quest.amount; + } + + async decorate({ + quest, + account, + data, + }: { + quest: QuestCustomDocument; + account?: TAccount; + data: Partial<TQuestCustomEntry>; + }) { + const entries = await this.findAllEntries({ quest, account }); + const identities = await this.findIdentities({ quest, account }); + const events = await this.findEvents({ quest, identities }); + const isAvailable = await this.isAvailable({ quest, account, data }); + const pointsAvailable = quest.limit ? (quest.limit - entries.length) * quest.amount : quest.amount; + + return { + ...quest, + eventName: '', // FK Deprecrates March 15th 2024 + isAvailable: isAvailable.result, + pointsAvailable, + entries, + events, + }; + } + + async getValidationResult({ + quest, + account, + }: { + quest: QuestCustomDocument; + account: TAccount; + data: Partial<TQuestCustomEntry>; + }): Promise<{ reason: string; result: boolean }> { + // See if there are identities + const identities = await this.findIdentities({ quest, account }); + if (!identities.length) { + return { + result: false, + reason: 'No identity connected to this account. Please ask for this in your community!', + }; + } + + // Find existing entries for this quest and check optional limit + const entries = await this.findAllEntries({ quest, account }); + if (quest.limit && entries.length >= quest.limit) { + return { result: false, reason: 'Quest entry limit has been reached' }; + } + + // Find events for this quest and the identities connected to the account + const events = await this.findEvents({ quest, identities }); + if (entries.length >= events.length) { + return { result: false, reason: 'Insufficient custom events found for this quest' }; + } + + if (entries.length < events.length) return { result: true, reason: '' }; + } + + private async findAllEntries({ quest, account }: { quest: QuestCustomDocument; account: TAccount }) { + if (!account) return []; + return await this.models.entry.find({ + questId: quest._id, + sub: account.sub, + isClaimed: true, + }); + } + + private async findIdentities({ quest, account }: { quest: QuestCustomDocument; account: TAccount }) { + if (!account || !account.sub) return []; + return await Identity.find({ poolId: quest.poolId, sub: account.sub }); + } + + private async findEvents({ quest, identities }: { quest: QuestCustomDocument; identities: IdentityDocument[] }) { + if (!identities.length) return []; + return await Event.find({ + identityId: { $in: identities.map(({ _id }) => String(_id)) }, + poolId: quest.poolId, + name: quest.eventName, + }).limit(quest.limit || null); + } +} diff --git a/apps/api/src/app/services/QuestDailyService.ts b/apps/api/src/app/services/QuestDailyService.ts new file mode 100644 index 000000000..ae0c488be --- /dev/null +++ b/apps/api/src/app/services/QuestDailyService.ts @@ -0,0 +1,206 @@ +import { Event, Identity, QuestDaily, QuestDailyEntry } from '@thxnetwork/api/models'; +import { IQuestService } from './interfaces/IQuestService'; + +const ONE_DAY_MS = 86400 * 1000; // 24 hours in milliseconds + +export default class QuestDailyService implements IQuestService { + models = { + quest: QuestDaily, + entry: QuestDailyEntry, + }; + + async findEntryMetadata({ quest }: { quest: TQuestDaily }) { + const uniqueParticipantIds = await this.models.entry + .countDocuments({ + questId: String(quest._id), + }) + .distinct('sub'); + + return { participantCount: uniqueParticipantIds.length }; + } + + async decorate({ + quest, + account, + data, + }: { + quest: TQuestDaily; + data: Partial<TQuestDailyEntry>; + account?: TAccount; + }): Promise< + TQuestDaily & { + isAvailable: boolean; + amount: number; + entries: TQuestDailyEntry[]; + claimAgainDuration: number; + } + > { + const amount = await this.getAmount({ quest, account }); + const entries = account ? await this.findEntries({ quest, account }) : []; + const claimAgainTime = entries.length ? new Date(entries[0].createdAt).getTime() + ONE_DAY_MS : null; + const now = Date.now(); + const isAvailable = await this.isAvailable({ quest, account, data }); + + return { + ...quest, + isAvailable: isAvailable.result, + amount, + entries, + claimAgainDuration: + claimAgainTime && claimAgainTime - now > 0 ? Math.floor((claimAgainTime - now) / 1000) : null, // Convert and floor to S, + }; + } + + async isAvailable({ + quest, + account, + data, + }: { + quest: TQuestDaily; + account: TAccount; + data: Partial<TQuestDailyEntry>; + }): Promise<TValidationResult> { + if (!account) return { result: true, reason: '' }; + + const now = Date.now(), + start = now - ONE_DAY_MS, + end = now; + + // Check for IP as we limit to 1 per IP per day (if an ip is passed) + if (data.metadata && data.metadata.ip) { + const isCompletedForIP = !!(await QuestDailyEntry.exists({ + 'questId': quest._id, + 'createdAt': { $gt: new Date(start), $lt: new Date(end) }, + 'metadata.ip': data.metadata.ip, + })); + if (isCompletedForIP) { + return { + result: false, + reason: 'You have completed this quest from this IP within the last 24 hours.', + }; + } + } + + const isCompleted = await QuestDailyEntry.findOne({ + questId: quest._id, + sub: account.sub, + createdAt: { $gt: new Date(start), $lt: new Date(end) }, + }); + if (!isCompleted) return { result: true, reason: '' }; + + return { result: false, reason: 'You have completed this quest within the last 24 hours.' }; + } + + async getAmount({ quest, account }: { quest: TQuestDaily; account: TAccount }): Promise<number> { + if (!account) return quest.amounts[0]; + + const claims = await this.findEntries({ quest, account }); + const amountIndex = + claims.length >= quest.amounts.length ? claims.length % quest.amounts.length : claims.length; + return quest.amounts[amountIndex]; + } + + async getValidationResult({ + quest, + account, + }: { + quest: TQuestDaily; + account: TAccount; + data: Partial<TQuestDailyEntry>; + }): Promise<TValidationResult> { + const now = Date.now(), + start = now - ONE_DAY_MS, + end = now; + + const entry = await QuestDailyEntry.findOne({ + questId: quest._id, + sub: account.sub, + createdAt: { $gt: new Date(start), $lt: new Date(end) }, + }); + + // If an entry has been found the user needs to wait + if (entry) { + return { result: false, reason: `Already completed within the last 24 hours.` }; + } + + // If no entry has been found and no event is required the entry is allowed to be created + if (!quest.eventName) { + return { result: true, reason: '' }; + } + + // If an event is required we check if there is an event found within the time window + const identities = await this.findIdentities({ quest, account }); + if (!identities.length) { + return { + result: false, + reason: 'No identity connected to this account. Please ask for this in your community!', + }; + } + + const identityIds = identities.map(({ _id }) => String(_id)); + const events = await Event.find({ + name: quest.eventName, + poolId: quest.poolId, + identityId: { $in: identityIds }, + createdAt: { $gt: new Date(start), $lt: new Date(end) }, + }); + + // If no events are found we invalidate + if (!events.length) { + return { result: false, reason: 'No events found for this account' }; + } + + // If events are found we validate true + else { + return { result: true, reason: '' }; + } + } + + private async findIdentities({ quest, account }: { quest: TQuestDaily; account: TAccount }) { + return await Identity.find({ sub: account.sub, poolId: quest.poolId }); + } + + private async findEntries({ account, quest }: { account: TAccount; quest: TQuestDaily }) { + const claims = []; + const now = Date.now(), + start = now - ONE_DAY_MS, + end = now; + + let lastEntry = await this.getLastEntry(account, quest, start, end); + if (!lastEntry) return []; + claims.push(lastEntry); + + while (lastEntry) { + const timestamp = new Date(lastEntry.createdAt).getTime(); + lastEntry = await QuestDailyEntry.findOne({ + questId: quest._id, + sub: account.sub, + createdAt: { + $gt: new Date(timestamp - ONE_DAY_MS * 2), + $lt: new Date(timestamp - ONE_DAY_MS), + }, + }); + if (!lastEntry) break; + claims.push(lastEntry); + } + + return claims; + } + + private async getLastEntry(account: TAccount, quest: TQuestDaily, start: number, end: number) { + let lastEntry = await QuestDailyEntry.findOne({ + questId: quest._id, + sub: account.sub, + createdAt: { $gt: new Date(start), $lt: new Date(end) }, + }); + + if (!lastEntry) { + lastEntry = await QuestDailyEntry.findOne({ + questId: quest._id, + sub: account.sub, + createdAt: { $gt: new Date(start - ONE_DAY_MS), $lt: new Date(end - ONE_DAY_MS) }, + }); + } + return lastEntry; + } +} diff --git a/apps/api/src/app/services/QuestDiscordService.ts b/apps/api/src/app/services/QuestDiscordService.ts new file mode 100644 index 000000000..d746ff5b4 --- /dev/null +++ b/apps/api/src/app/services/QuestDiscordService.ts @@ -0,0 +1,193 @@ +import { QuestSocialEntry, QuestSocial, DiscordMessage } from '@thxnetwork/api/models'; +import { QuestSocialRequirement } from '@thxnetwork/common/enums'; +import { IQuestService } from './interfaces/IQuestService'; +import { requirementMap } from './maps/quests'; +import QuestSocialService from './QuestSocialService'; +import QuestService from './QuestService'; + +type TRestartDates = { now: Date; start: Date; endDay: Date; end: Date }; + +export default class QuestDiscordService implements IQuestService { + models = { + quest: QuestSocial, + entry: QuestSocialEntry, + }; + + findEntryMetadata(options: { quest: TQuestSocial }) { + return {}; + } + + async decorate({ + quest, + account, + data, + }: { + quest: TQuestSocial; + account: TAccount; + data: Partial<TQuestSocialEntry>; + }): Promise< + TQuestSocial & { + messages: TDiscordMessage[]; + restartDates: TRestartDates; + amount: number; + isAvailable: boolean; + } + > { + const amount = await this.getAmount({ quest, account }); + const isAvailable = await this.isAvailable({ quest, account, data }); + const interactionMap = { + [QuestSocialRequirement.DiscordMessage]: this.getDiscordMessageParams.bind(this), + [QuestSocialRequirement.DiscordGuildJoined]: this.getDiscordParams.bind(this), + [QuestSocialRequirement.DiscordGuildRole]: this.getDiscordParams.bind(this), + }; + const extraParams = await interactionMap[quest.interaction]({ quest, account }); + + return { + ...quest, + amount, + isAvailable: isAvailable.result, + contentMetadata: quest.contentMetadata && JSON.parse(quest.contentMetadata), + ...extraParams, + }; + } + + async isAvailable({ + quest, + account, + }: { + quest: TQuestSocial; + account?: TAccount; + data: Partial<TQuestSocialEntry>; + }): Promise<TValidationResult> { + const map = { + [QuestSocialRequirement.DiscordMessage]: this.isAvailableMessage.bind(this), + [QuestSocialRequirement.DiscordGuildJoined]: this.isAvailableDefault.bind(this), + [QuestSocialRequirement.DiscordGuildRole]: this.isAvailableDefault.bind(this), + }; + return await map[quest.interaction]({ quest, account }); + } + + private async isAvailableDefault({ + quest, + account, + data, + }: { + quest: TQuestSocial; + account?: TAccount; + data: Partial<TQuestSocialEntry>; + }) { + if (!account) return { result: true, reason: '' }; + + // We use the default more generic QuestSocialService here since we want to + // validate for platformUserIds as well + return await new QuestSocialService().isAvailable({ quest, account, data }); + } + + private async isAvailableMessage({ quest, account }: { quest: TQuestSocial; account?: TAccount }) { + const { pointsAvailable } = await this.getMessagePoints({ quest, account }); + const isAvailable = pointsAvailable > 0; + if (isAvailable) return { result: true, reason: '' }; + + return { result: false, reason: 'You have not earned any points with messages yet.' }; + } + + async getAmount({ account, quest }: { quest: TQuestSocial; account?: TAccount }): Promise<number> { + const interactionMap = { + [QuestSocialRequirement.DiscordMessage]: this.getMessagePoints.bind(this), + [QuestSocialRequirement.DiscordGuildJoined]: this.getPoints.bind(this), + [QuestSocialRequirement.DiscordGuildRole]: this.getPoints.bind(this), + }; + const { pointsAvailable } = await interactionMap[quest.interaction]({ quest, account }); + return pointsAvailable; + } + + async getValidationResult(options: { + quest: TQuestSocial; + account: TAccount; + data: Partial<TQuestSocialEntry>; + }): Promise<TValidationResult> { + if (!options.quest.interaction) return { result: false, reason: '' }; + return await requirementMap[options.quest.interaction](options.account, options.quest); + } + + private getRestartDates(quest: TQuestSocial) { + const { days } = JSON.parse(quest.contentMetadata); + const now = new Date(); + const questCreatedAt = new Date(quest.createdAt); + const totalDaysRunning = Math.floor( + Math.ceil(now.getTime() / 1000 - questCreatedAt.getTime() / 1000) / 60 / 60 / 24, + ); + const daysRunning = totalDaysRunning % days; + const msRunning = daysRunning * 24 * 60 * 60 * 1000; + + const start = new Date(now.getTime() - msRunning); + start.setUTCHours(0, 0, 0, 0); + + const end = new Date(start.getTime() + days * 24 * 60 * 60 * 1000); + const endDay = new Date(now); + endDay.setUTCHours(23, 59, 59, 999); + + return { now, start, endDay, end }; + } + + private async getDiscordParams({ quest }: { quest: TQuestSocial; account: TAccount }) { + return { pointsAvailable: quest.amount }; + } + + private async getDiscordMessageParams({ quest, account }: { quest: TQuestSocial; account: TAccount }) { + const restartDates = this.getRestartDates(quest); + const messages = await this.getMessages({ account, quest, start: restartDates.start }); + const points = await this.getMessagePoints({ + quest, + account, + }); + + return { + restartDates, + messages, + ...points, + }; + } + + private async getMessages({ quest, account, start }: { quest: TQuestSocial; account: TAccount; start: Date }) { + if (!account) return []; + + const userId = QuestService.findUserIdForInteraction(account, quest.interaction); + return await DiscordMessage.find({ + guildId: quest.content, + memberId: userId, + createdAt: { $gte: new Date(start).toISOString() }, + }); + } + + private async getPoints({ quest }) { + return { pointsAvailable: quest.amount }; + } + + private async getMessagePoints({ quest, account }) { + if (!account) return { pointsAvailable: 0, pointsClaimed: 0 }; + + const { start, end } = this.getRestartDates(quest); + const platformUserId = QuestService.findUserIdForInteraction(account, quest.interaction); + const claims = await QuestSocialEntry.find({ + 'questId': String(quest._id), + 'metadata.platformUserId': platformUserId, + 'createdAt': { + $gte: start, + $lt: end, + }, + }).sort({ createdAt: -1 }); + const [claim] = claims; + const pointsClaimed = claims.reduce((total, claim) => total + Number(claim.amount), 0); + + // Only find messages created after the last claim if one exists + const messages = await DiscordMessage.find({ + guildId: quest.content, + memberId: platformUserId, + createdAt: { $gte: claim ? claim.createdAt : start, $lt: end }, + }); + const pointsAvailable = messages.length * quest.amount; + + return { pointsClaimed, pointsAvailable }; + } +} diff --git a/apps/api/src/app/services/QuestGitcoinService.ts b/apps/api/src/app/services/QuestGitcoinService.ts new file mode 100644 index 000000000..73825fe5b --- /dev/null +++ b/apps/api/src/app/services/QuestGitcoinService.ts @@ -0,0 +1,75 @@ +import { QuestGitcoin, QuestGitcoinEntry } from '@thxnetwork/api/models'; +import { IQuestService } from './interfaces/IQuestService'; +import GitcoinService from './GitcoinService'; + +export default class QuestGitcoinService implements IQuestService { + models = { + quest: QuestGitcoin, + entry: QuestGitcoinEntry, + }; + + findEntryMetadata(options: { quest: TQuestGitcoin }) { + return {}; + } + + async decorate({ + quest, + account, + data, + }: { + quest: TQuestGitcoin; + account?: TAccount; + data: Partial<TQuestGitcoinEntry>; + }): Promise<TQuestGitcoin & { isAvailable: boolean }> { + const isAvailable = await this.isAvailable({ quest, account, data }); + return { ...quest, isAvailable: isAvailable.result }; + } + + async isAvailable({ + quest, + account, + data, + }: { + quest: TQuestGitcoin; + account?: TAccount; + data: Partial<TQuestGitcoinEntry>; + }): Promise<TValidationResult> { + if (!account) return { result: true, reason: '' }; + + const ids: { [key: string]: string }[] = [{ sub: account.sub }]; + if (data.metadata && data.metadata.address) ids.push({ 'metadata.address': data.metadata.address }); + + const isCompleted = await QuestGitcoinEntry.exists({ + questId: quest._id, + $or: ids, + }); + if (!isCompleted) return { result: true, reason: '' }; + + return { result: false, reason: 'You have completed this quest with this account and/or address already.' }; + } + + async getAmount({ quest }: { quest: TQuestGitcoin; account: TAccount }): Promise<number> { + return quest.amount; + } + + async getValidationResult({ + quest, + data, + }: { + quest: TQuestGitcoin; + account: TAccount; + data: Partial<TQuestGitcoinEntry>; + }): Promise<TValidationResult> { + if (!data.metadata.address) return { result: false, reason: 'Could not find an address during validation.' }; + if (data.metadata.score < quest.score) { + const score = data.metadata.score.toString() || 0; + const reason = `Your score ${score}/100 does not meet the minimum of ${quest.score}/100.`; + return { result: false, reason }; + } + if (data.metadata.score >= quest.score) return { result: true, reason: '' }; + } + + async getScore(scorerId: number, address: string) { + return await GitcoinService.getScoreUniqueHumanity(scorerId, address); + } +} diff --git a/apps/api/src/app/services/QuestInviteService.ts b/apps/api/src/app/services/QuestInviteService.ts new file mode 100644 index 000000000..61c31393b --- /dev/null +++ b/apps/api/src/app/services/QuestInviteService.ts @@ -0,0 +1,44 @@ +import { QuestInvite, QuestInviteEntry } from '@thxnetwork/api/models'; +import { IQuestService } from './interfaces/IQuestService'; + +export default class QuestInviteService implements IQuestService { + models = { + quest: QuestInvite, + entry: QuestInviteEntry, + }; + + findEntryMetadata(options: { quest: TQuestInvite }) { + return {}; + } + + async decorate({ quest }: { quest: TQuestInvite; data: Partial<TQuestInviteEntry> }): Promise<TQuestInvite> { + return { + ...quest, + pathname: quest.pathname, + successUrl: quest.successUrl, + }; + } + + async isAvailable(options: { + quest: TQuestInvite; + account?: TAccount; + data: Partial<TQuestInviteEntry>; + }): Promise<TValidationResult> { + return { result: false, reason: 'Not implemented' }; + } + + async getAmount({ quest }: { quest: TQuestInvite; account: TAccount }): Promise<number> { + return quest.amount; + } + + async getValidationResult(options: { + quest: TQuestInvite; + account: TAccount; + data: Partial<TQuestInviteEntry>; + }): Promise<TValidationResult> { + return { + result: false, + reason: 'Sorry, support not yet implemented...', + }; + } +} diff --git a/apps/api/src/app/services/QuestService.ts b/apps/api/src/app/services/QuestService.ts new file mode 100644 index 000000000..e33ee0b33 --- /dev/null +++ b/apps/api/src/app/services/QuestService.ts @@ -0,0 +1,260 @@ +import { JobType, QuestSocialRequirement, QuestVariant } from '@thxnetwork/common/enums'; +import { PoolDocument, Participant } from '@thxnetwork/api/models'; +import { v4 } from 'uuid'; +import { agenda } from '../util/agenda'; +import { logger } from '../util/logger'; +import { Job } from '@hokify/agenda'; +import { serviceMap } from './interfaces/IQuestService'; +import { tokenInteractionMap } from './maps/quests'; +import { NODE_ENV } from '../config/secrets'; +import PoolService from './PoolService'; +import NotificationService from './NotificationService'; +import PointBalanceService from './PointBalanceService'; +import LockService from './LockService'; +import ImageService from './ImageService'; +import AccountProxy from '../proxies/AccountProxy'; +import ParticipantService from './ParticipantService'; +import THXService from './THXService'; + +export default class QuestService { + static async count({ poolId }) { + const variants = Object.keys(QuestVariant).filter((v) => !isNaN(Number(v))); + const counts = await Promise.all( + variants.map(async (variant: string) => { + const Quest = serviceMap[variant].models.quest; + return await Quest.countDocuments({ poolId, isPublished: true }); + }), + ); + return counts.reduce((acc, count) => acc + count, 0); + } + + static async list({ pool, data, account }: { pool: PoolDocument; data: Partial<TQuestEntry>; account?: TAccount }) { + const questVariants = Object.keys(QuestVariant).filter((v) => !isNaN(Number(v))); + const author = await AccountProxy.findById(pool.sub); + const callback: any = async (variant: QuestVariant) => { + const Quest = serviceMap[variant].models.quest; + const quests = await Quest.find({ + poolId: pool._id, + variant, + isPublished: true, + $or: [ + // Include quests with expiryDate less than or equal to now + { expiryDate: { $exists: true, $gte: new Date() } }, + // Include quests with no expiryDate + { expiryDate: { $exists: false } }, + ], + }); + + return await Promise.all( + quests.map(async (q) => { + try { + const quest = q.toJSON() as TQuest; + const decorated = await serviceMap[variant].decorate({ quest, account, data }); + const isLocked = await LockService.getIsLocked(quest.locks, account); + const isExpired = this.isExpired(quest); + const QuestEntry = serviceMap[variant].models.entry; + const distinctSubs = await QuestEntry.countDocuments({ questId: q.id }).distinct('sub'); + return { + ...decorated, + entryCount: distinctSubs.length, + author: { username: author.username }, + isLocked, + isExpired, + }; + } catch (error) { + logger.error(error); + } + }), + ); + }; + + return await Promise.all(questVariants.map(callback)); + } + + static async update(quest: TQuest, updates: Partial<TQuest>, file?: Express.Multer.File) { + if (file) { + updates.image = await ImageService.upload(file); + } + + // We only want to notify when the quest is set to published (and not updated while published already) + if (updates.isPublished && Boolean(updates.isPublished) !== quest.isPublished) { + await NotificationService.notify(quest.variant, { + ...quest, + ...updates, + image: updates.image || quest.image, + }); + } + + return await this.updateById(quest.variant, quest._id, updates); + } + + static async create(variant: QuestVariant, poolId: string, data: Partial<TQuest>, file?: Express.Multer.File) { + if (file) { + data.image = await ImageService.upload(file); + } + + const Quest = serviceMap[variant].models.quest; + const quest = await Quest.create({ ...data, poolId, variant, uuid: v4() }); + + if (data.isPublished) { + await NotificationService.notify(variant, quest); + } + + return quest; + } + + static findById(variant: QuestVariant, questId: string) { + const Quest = serviceMap[variant].models.quest; + return Quest.findById(questId); + } + + static updateById(variant: QuestVariant, questId: string, options: Partial<TQuest>) { + const Quest = serviceMap[variant].models.quest; + return Quest.findByIdAndUpdate(questId, options, { new: true }); + } + + static getAmount(variant: QuestVariant, quest: TQuest, account: TAccount) { + return serviceMap[variant].getAmount({ quest, account }); + } + + static isExpired(quest: TQuest) { + return quest.expiryDate ? new Date(quest.expiryDate).getTime() < Date.now() : false; + } + + static async isAvailable( + variant: QuestVariant, + options: { + quest: TQuest; + account?: TAccount; + data: Partial<TQuestEntry & { rpc: string }>; + }, + ): Promise<TValidationResult> { + if (!options.quest.isPublished) { + return { result: false, reason: 'Quest has not been published.' }; + } + + const isExpired = this.isExpired(options.quest); + if (isExpired) return { result: false, reason: 'Quest has expired.' }; + + const isLocked = await LockService.getIsLocked(options.quest.locks, options.account); + if (isLocked) return { result: false, reason: 'Quest is locked.' }; + + return await serviceMap[variant].isAvailable(options); + } + + static async isRealUser( + variant: QuestVariant, + options: { quest: TQuest; account: TAccount; data: Partial<TQuestEntry & { recaptcha: string }> }, + ) { + // Skip recaptcha check in test environment + if (NODE_ENV === 'test') return { result: true, reasons: '' }; + + // Define the recaptcha action for this quest variant + const recaptchaAction = `QUEST_${QuestVariant[variant].toUpperCase()}_ENTRY_CREATE`; + + // Update the participant's risk score + const { riskAnalysis } = await ParticipantService.updateRiskScore(options.account, options.quest.poolId, { + token: options.data.recaptcha, + recaptchaAction, + }); + + logger.info( + 'ReCaptcha result' + + JSON.stringify({ + sub: options.account.sub, + poolId: options.quest.poolId, + riskAnalysis, + recaptchaAction, + }), + ); + + // Defaults: 0.1, 0.3, 0.7 and 0.9. Ranges from 0 (Bot) to 1 (User) + if (riskAnalysis.score >= 0.9) { + return { result: true, reasons: '' }; + } + + return { result: false, reason: 'This request has been indentified as potentially automated.' }; + } + + static async getValidationResult( + variant: QuestVariant, + options: { + quest: TQuest; + account: TAccount; + data: Partial<TQuestEntry>; + }, + ) { + const isAvailable = await this.isAvailable(variant, options); + if (!isAvailable.result) return isAvailable; + + return await serviceMap[variant].getValidationResult(options); + } + + static async createEntryJob(job: Job) { + try { + const { variant, questId, sub, data } = job.attrs.data as any; + const Entry = serviceMap[Number(variant)].models.entry; + const account = await AccountProxy.findById(sub); + const quest = await this.findById(variant, questId); + const pool = await PoolService.getById(quest.poolId); + const amount = await this.getAmount(variant, quest, account); + + // Test availabily of quest once more as it could be completed by a job that was scheduled already + // if the jobs were created in parallel. + const isAvailable = await this.isAvailable(variant, { quest, account, data }); + if (!isAvailable.result) throw new Error(isAvailable.reason); + + // Create the quest entry + const entry = await Entry.create({ + ...data, + sub: account.sub, + amount, + questId: String(quest._id), + poolId: pool._id, + uuid: v4(), + } as TQuestEntry); + if (!entry) throw new Error('Entry creation failed.'); + + // Should make sure quest entry is properly created + await PointBalanceService.add(pool, account, amount); + await NotificationService.sendQuestEntryNotification(pool, quest, account, amount); + + // Register THX onboarding campaign event + await THXService.createEvent(account, 'quest_entry_created'); + + // Update participant ranks async + agenda.now(JobType.UpdateParticipantRanks, { poolId: pool._id }); + } catch (error) { + logger.error(error); + } + } + + static findUserIdForInteraction(account: TAccount, interaction: QuestSocialRequirement) { + if (typeof interaction === 'undefined') return; + const { kind } = tokenInteractionMap[interaction]; + const token = account.tokens.find((token) => token.kind === kind); + + return token && token.userId; + } + + static async findEntries(quest: TQuest, { page = 1, limit = 25 }: { page: number; limit: number }) { + const skip = (page - 1) * limit; + const Entry = serviceMap[quest.variant].models.entry; + const total = await Entry.countDocuments({ questId: quest._id }); + const entries = await Entry.find({ questId: quest._id }).limit(limit).skip(skip); + const subs = entries.map((entry) => entry.sub); + const accounts = await AccountProxy.find({ subs }); + const participants = await Participant.find({ poolId: quest.poolId }); + const promises = entries.map(async (entry) => ParticipantService.decorate(entry, { accounts, participants })); + const results = await Promise.allSettled(promises); + const meta = await serviceMap[quest.variant].findEntryMetadata({ quest }); + + return { + total, + limit, + page, + meta, + results: results.filter((result) => result.status === 'fulfilled').map((result: any) => result.value), + }; + } +} diff --git a/apps/api/src/app/services/QuestSocialService.ts b/apps/api/src/app/services/QuestSocialService.ts new file mode 100644 index 000000000..289f6e03c --- /dev/null +++ b/apps/api/src/app/services/QuestSocialService.ts @@ -0,0 +1,100 @@ +import { QuestSocial, QuestSocialDocument, QuestSocialEntry } from '@thxnetwork/api/models'; +import { WalletDocument } from '@thxnetwork/api/models/Wallet'; +import { IQuestService } from './interfaces/IQuestService'; +import { requirementMap } from './maps/quests'; +import { logger } from '../util/logger'; +import { QuestVariant } from '@thxnetwork/common/enums'; + +export default class QuestSocialService implements IQuestService { + models = { + quest: QuestSocial, + entry: QuestSocialEntry, + }; + + async decorate({ + quest, + account, + data, + }: { + quest: TQuestSocial; + account?: TAccount; + data: Partial<TQuestSocialEntry>; + }): Promise<TQuestSocial & { isAvailable: boolean }> { + const isAvailable = await this.isAvailable({ quest, account, data }); + + return { + ...quest, + isAvailable: isAvailable.result, + contentMetadata: quest.contentMetadata && JSON.parse(quest.contentMetadata), + }; + } + + async isAvailable({ + quest, + account, + data, + }: { + quest: TQuestSocial; + account: TAccount; + data: Partial<TQuestSocialEntry>; + }): Promise<TValidationResult> { + if (!account) return { result: true, reason: '' }; + + // We validate for both here since there are entries that only contain a sub + // and should not be claimed again. + const ids: any[] = [{ sub: account.sub }]; + if (data && data.metadata && data.metadata.platformUserId) + ids.push({ platformUserId: data.metadata.platformUserId }); + + // If no entry exist the quest is available + const isCompleted = await QuestSocialEntry.exists({ + questId: quest._id, + $or: ids, + }); + if (!isCompleted) return { result: true, reason: '' }; + + return { result: false, reason: 'You have completed this quest with this (connected) account already.' }; + } + + async getAmount({ quest }: { quest: TQuestSocial; wallet: WalletDocument; account: TAccount }): Promise<number> { + return quest.amount; + } + + async getValidationResult({ + quest, + account, + }: { + quest: TQuestSocial; + account: TAccount; + data: Partial<TQuestSocialEntry>; + }): Promise<TValidationResult> { + try { + // Check quest requirements + const validationResult = await requirementMap[quest.interaction](account, quest); + return validationResult || { result: true, reason: '' }; + } catch (error) { + logger.error(error); + return { result: false, reason: 'We were unable to confirm the requirements for this quest.' }; + } + } + + async findEntryMetadata({ quest }: { quest: QuestSocialDocument }) { + const reachTotal = await this.getTwitterFollowerCount(quest); + const uniqueParticipantIds = await QuestSocialEntry.find({ + questId: String(quest._id), + }).distinct('sub'); + + return { reachTotal, participantCount: uniqueParticipantIds.length }; + } + + async getTwitterFollowerCount(quest: QuestSocialDocument) { + if (quest.variant !== QuestVariant.Twitter) return; + + const [result] = await QuestSocialEntry.aggregate([ + { $match: { questId: String(quest._id) } }, + { $group: { _id: null, totalFollowersCount: { $sum: '$publicMetrics.followersCount' } } }, + ]); + + return result ? result.totalFollowersCount : 0; + } +} diff --git a/apps/api/src/app/services/QuestWeb3Service.ts b/apps/api/src/app/services/QuestWeb3Service.ts new file mode 100644 index 000000000..d38ea484b --- /dev/null +++ b/apps/api/src/app/services/QuestWeb3Service.ts @@ -0,0 +1,116 @@ +import { ethers } from 'ethers'; +import { QuestWeb3Entry, QuestWeb3 } from '@thxnetwork/api/models'; +import { logger } from '@thxnetwork/api/util/logger'; +import { IQuestService } from './interfaces/IQuestService'; +import { BigNumber } from 'alchemy-sdk'; + +export default class QuestWeb3Service implements IQuestService { + models = { + quest: QuestWeb3, + entry: QuestWeb3Entry, + }; + + findEntryMetadata(options: { quest: TQuestWeb3 }) { + return {}; + } + + async decorate({ + quest, + account, + data, + }: { + quest: TQuestWeb3; + data: Partial<TQuestWeb3Entry>; + account?: TAccount; + }): Promise<TQuestWeb3 & { isAvailable: boolean }> { + const isAvailable = await this.isAvailable({ quest, account, data }); + + return { + ...quest, + isAvailable: isAvailable.result, + amount: quest.amount, + contracts: quest.contracts, + methodName: quest.methodName, + threshold: quest.threshold, + }; + } + + async isAvailable({ + quest, + account, + data, + }: { + quest: TQuestWeb3; + account: TAccount; + data: Partial<TQuestWeb3Entry>; + }): Promise<TValidationResult> { + if (!account) return { result: true, reason: '' }; + + const ids: any[] = [{ sub: account.sub }]; + if (data.metadata && data.metadata.address) ids.push({ 'metadata.address': data.metadata.address }); + + const isCompleted = await QuestWeb3Entry.exists({ + questId: quest._id, + $or: ids, + }); + if (!isCompleted) return { result: true, reason: '' }; + + return { result: false, reason: 'You have completed this quest with this account and/or address already.' }; + } + + async getAmount({ quest }: { quest: TQuestWeb3; account: TAccount }): Promise<number> { + return quest.amount; + } + + async getValidationResult({ + quest, + account, + data, + }: { + quest: TQuestWeb3; + account: TAccount; + data: Partial<TQuestWeb3Entry>; + }): Promise<TValidationResult> { + const isCompleted = await QuestWeb3Entry.exists({ + questId: quest._id, + $or: [{ sub: account.sub }, { 'metadata.address': data.metadata.address }], + }); + if (isCompleted) return { result: false, reason: 'You have claimed this quest already' }; + + const threshold = BigNumber.from(quest.threshold); + const result = BigNumber.from(data.metadata.callResult); + if (result.lt(threshold)) { + return { result: false, reason: 'Result does not meet the threshold' }; + } + + return { result: true, reason: '' }; + } + + static async getCallResult({ + quest, + data, + }: { + quest: TQuestWeb3; + account: TAccount; + data: Partial<TQuestWeb3Entry>; + }) { + const { rpc, chainId, address } = data.metadata; + const contract = quest.contracts.find((c) => c.chainId === chainId); + if (!contract) return { result: false, reason: 'Smart contract not found.' }; + + const contractInstance = new ethers.Contract( + contract.address, + ['function ' + quest.methodName + '(address) view returns (uint256)'], + new ethers.providers.JsonRpcProvider(rpc), + ); + + try { + const value = await contractInstance[quest.methodName](address); + + return { result: true, reason: '', value }; + } catch (error) { + logger.error(error); + return { result: false, reason: `Smart contract call on ${quest.methodName} failed` }; + } + } +} diff --git a/apps/api/src/app/services/QuestWebhookService.ts b/apps/api/src/app/services/QuestWebhookService.ts new file mode 100644 index 000000000..798d4e0e3 --- /dev/null +++ b/apps/api/src/app/services/QuestWebhookService.ts @@ -0,0 +1,114 @@ +import { + QuestWebhook, + QuestWebhookDocument, + QuestWebhookEntry, + Identity, + WalletDocument, + Webhook, +} from '@thxnetwork/api/models'; +import { IQuestService } from './interfaces/IQuestService'; +import WebhookService from './WebhookService'; + +export default class QuestWebhookService implements IQuestService { + models = { + quest: QuestWebhook, + entry: QuestWebhookEntry, + }; + + async findEntryMetadata({ quest }: { quest: QuestWebhookDocument }) { + const uniqueParticipantIds = await QuestWebhookEntry.countDocuments({ + questId: String(quest._id), + }).distinct('sub'); + + return { participantCount: uniqueParticipantIds.length }; + } + + async isAvailable({ + quest, + account, + }: { + quest: QuestWebhookDocument; + wallet?: WalletDocument; + account?: TAccount; + data: Partial<TQuestWebhookEntry>; + }): Promise<TValidationResult> { + const entries = await this.findAllEntries({ quest, account }); + if (entries.length) { + return { result: false, reason: 'Quest entry limit has been reached.' }; + } + + return { result: true, reason: '' }; + } + + async getAmount({ quest }: { quest: QuestWebhookDocument; wallet: WalletDocument; account: TAccount }) { + return quest.amount; + } + + async decorate({ + quest, + account, + data, + }: { + quest: QuestWebhookDocument; + account?: TAccount; + data: Partial<TQuestWebhookEntry>; + }) { + const entries = await this.findAllEntries({ quest, account }); + const identities = await this.findIdentities({ quest, account }); + const isAvailable = await this.isAvailable({ quest, account, data }); + + return { + ...quest, + identities, + isAvailable: isAvailable.result, + entries, + }; + } + + async getValidationResult({ + quest, + account, + }: { + quest: QuestWebhookDocument; + account: TAccount; + data: Partial<TQuestWebhookEntry>; + }): Promise<{ reason: string; result: boolean; data?: any }> { + // See if there are identities + const identities = await this.findIdentities({ quest, account }); + if (!identities.length) { + return { + result: false, + reason: 'No identity connected to this account. Please ask for this in your community!', + }; + } + + const webhook = await Webhook.findById(quest.webhookId); + if (!webhook) return { result: false, reason: 'Webhook no longer available.' }; + + const data = await WebhookService.request(webhook, account, quest.metadata); + if (!data) return { result: false, reason: 'Webhook validation returned nothing.' }; + if (!data.result) return { result: false, reason: 'Webhook validation was negative.' }; + if (data.result) { + return { + result: true, + reason: '', + data, + }; + } + + return { result: false, reason: 'Webhook validation request failed.' }; + } + + private async findAllEntries({ quest, account }: { quest: QuestWebhookDocument; account: TAccount }) { + if (!account) return []; + return await this.models.entry.find({ + questId: quest._id, + sub: account.sub, + }); + } + + private async findIdentities({ quest, account }: { quest: QuestWebhookDocument; account: TAccount }) { + if (!account || !account.sub) return []; + return await Identity.find({ poolId: quest.poolId, sub: account.sub }); + } +} diff --git a/apps/api/src/app/services/ReCaptchaService.ts b/apps/api/src/app/services/ReCaptchaService.ts new file mode 100644 index 000000000..7af44d027 --- /dev/null +++ b/apps/api/src/app/services/ReCaptchaService.ts @@ -0,0 +1,39 @@ +import axios from 'axios'; +import { GCLOUD_PROJECT_ID, GCLOUD_RECAPTCHA_API_KEY, GCLOUD_RECAPTCHA_SITE_KEY } from '../config/secrets'; +import { BadRequestError } from '../util/errors'; + +export default class ReCaptchaService { + static async getRiskAnalysis({ token, recaptchaAction }) { + const url = new URL('https://recaptchaenterprise.googleapis.com'); + url.pathname = `/v1/projects/${GCLOUD_PROJECT_ID}/assessments`; + + const { data } = await axios({ + method: 'POST', + url: url.toString(), + data: { + event: { + token, + expectedAction: recaptchaAction, + siteKey: GCLOUD_RECAPTCHA_SITE_KEY, + }, + }, + params: { + key: GCLOUD_RECAPTCHA_API_KEY, + }, + }); + + // Check if the token is valid. + if (!data.tokenProperties.valid) { + throw new BadRequestError('Invalid ReCAPTCHA token.'); + } + + // Check if the expected action was executed. + if (data.tokenProperties.action !== recaptchaAction) { + throw new BadRequestError('Invalid ReCAPTCHA action.'); + } + + // Get the risk score and the reason(s). + // https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment + return data.riskAnalysis; + } +} diff --git a/apps/api/src/app/services/RewardCoinService.ts b/apps/api/src/app/services/RewardCoinService.ts new file mode 100644 index 000000000..405895c06 --- /dev/null +++ b/apps/api/src/app/services/RewardCoinService.ts @@ -0,0 +1,140 @@ +import { + ERC20, + ERC20Document, + RewardCoin, + RewardCoinDocument, + Transaction, + WalletDocument, +} from '@thxnetwork/api/models'; +import { RewardCoinPayment } from '@thxnetwork/api/models'; +import { IRewardService } from './interfaces/IRewardService'; +import { ChainId, ERC20Type, TransactionState } from '@thxnetwork/common/enums'; +import { BigNumber } from 'alchemy-sdk'; +import AccountProxy from '../proxies/AccountProxy'; +import ERC20Service from './ERC20Service'; +import MailService from './MailService'; +import PoolService from './PoolService'; +import { toWei } from 'web3-utils'; + +export default class RewardCoinService implements IRewardService { + models = { + reward: RewardCoin, + payment: RewardCoinPayment, + }; + + async decorate({ reward }) { + const erc20 = await ERC20.findById(reward.erc20Id); + return { ...reward.toJSON(), erc20 }; + } + + async decoratePayment(payment: TBaseRewardPayment) { + return payment; + } + + findById(id: string) { + return this.models.reward.findById(id); + } + + async create(data: Partial<TRewardCoin>) { + const erc20 = await this.getERC20(data.erc20Id); + await this.addMinter(erc20, data.poolId); + + return await this.models.reward.create(data); + } + + async update(reward: RewardCoinDocument, updates: Partial<TRewardCoin>) { + const erc20 = await this.getERC20(updates.erc20Id); + await this.addMinter(erc20, reward.poolId); + + return await this.models.reward.findByIdAndUpdate(reward._id, updates, { new: true }); + } + + async remove(reward: RewardCoinDocument) { + await this.models.reward.findOneAndDelete(reward._id); + } + + async createPayment({ + reward, + safe, + wallet, + }: { + reward: TRewardCoin; + safe: WalletDocument; + wallet?: WalletDocument; + }) { + if (!wallet) return { result: false, reason: 'Wallet not found' }; + + const erc20 = await ERC20.findById(reward.erc20Id); + if (!erc20) return { result: false, reason: 'ERC20 not found' }; + + // TODO Wei should be determined in the FE + const amount = toWei(reward.amount as string); + + // Transfer ERC20 from safe to wallet + await ERC20Service.transferFrom(erc20, safe, wallet.address, amount); + + // Register the payment + await RewardCoinPayment.create({ + rewardId: reward._id, + sub: wallet.sub, + walletId: wallet._id, + poolId: reward.poolId, + amount: reward.pointPrice, + }); + } + + async getValidationResult({ reward, safe }: { reward: RewardCoinDocument; safe: WalletDocument }) { + const erc20 = await ERC20.findById(reward.erc20Id); + if (!erc20) throw new Error('ERC20 not found'); + + // Check if there are pending transactions that are not mined or failed. + const txs = await Transaction.find({ + walletId: safe.id, + $or: [ + { state: TransactionState.Confirmed }, + { state: TransactionState.Sent }, + { state: TransactionState.Queued }, + ], + }).sort({ createdAt: 'asc' }); + if (txs.length) { + return { result: false, reason: `Found ${txs.length} pending transactions, please try again later.` }; + } + + // Check balances + const balanceOfPool = await erc20.contract.methods.balanceOf(safe.address).call(); + const isTransferable = [ERC20Type.Unknown, ERC20Type.Limited].includes(erc20.type); + const isBalanceInsufficient = BigNumber.from(balanceOfPool).lt(BigNumber.from(toWei(reward.amount))); + + // Notifiy the campaign owner if token is transferrable and balance is insufficient + if (isTransferable && isBalanceInsufficient) { + const owner = await AccountProxy.findById(safe.sub); + const html = `Not enough ${erc20.symbol} available in campaign contract ${safe.address}. Please top up on ${ + ChainId[erc20.chainId] + }`; + + // Send email to campaign owner + await MailService.send(owner.email, `⚠️ Out of ${erc20.symbol}!"`, html); + + return { + result: false, + reason: `We have notified the campaign owner that there is insufficient ${erc20.symbol} in the campaign wallet. Please try again later!`, + }; + } + + return { result: true, reason: '' }; + } + + private getERC20(erc20Id: TERC20) { + return ERC20.findById(erc20Id); + } + + private async addMinter(erc20: ERC20Document, poolId: string) { + if (erc20.type !== ERC20Type.Unlimited) return; + + const { safe } = await PoolService.getById(poolId); + const isMinter = await ERC20Service.isMinter(erc20, safe.address); + if (!isMinter) { + await ERC20Service.addMinter(erc20, safe.address); + } + } +} diff --git a/apps/api/src/app/services/RewardCouponService.ts b/apps/api/src/app/services/RewardCouponService.ts new file mode 100644 index 000000000..403b70151 --- /dev/null +++ b/apps/api/src/app/services/RewardCouponService.ts @@ -0,0 +1,75 @@ +import { CouponCode, RewardCoupon, RewardCouponPayment } from '../models'; +import { IRewardService } from './interfaces/IRewardService'; + +export default class RewardCouponService implements IRewardService { + models = { + reward: RewardCoupon, + payment: RewardCouponPayment, + }; + + async decorate({ reward }) { + const couponCodes = await CouponCode.find({ couponRewardId: reward._id }); + const progress = { + count: await this.models.payment.countDocuments({ + rewardId: reward._id, + }), + limit: couponCodes.length, + }; + + return { ...reward.toJSON(), progress, limit: couponCodes.length }; + } + + async decoratePayment(payment: TRewardPayment) { + const code = await CouponCode.findById(payment.couponCodeId); + return { ...payment.toJSON(), code: code && code.code }; + } + + async getValidationResult({ reward }: { reward: TReward; account?: TAccount }) { + const couponCode = await CouponCode.findOne({ couponRewardId: String(reward._id), sub: { $exists: false } }); + if (!couponCode) return { result: false, reason: 'No more coupon codes available' }; + + return { result: true, reason: '' }; + } + + async create(data: Partial<TReward>) { + const reward = await this.models.reward.create(data); + await this.createCouponCodes(reward, data.codes); + return reward; + } + + private async createCouponCodes(reward: TRewardCoupon, codes: string[]) { + await Promise.all( + codes.map(async (code: string) => await CouponCode.create({ code, couponRewardId: reward._id })), + ); + } + + async update(reward: TReward, updates: Partial<TReward>): Promise<TReward> { + await this.createCouponCodes(reward, updates.codes); + return this.models.reward.findByIdAndUpdate(reward, updates, { new: true }); + } + + remove(reward: TReward): Promise<void> { + return this.models.reward.findByIdAndDelete(reward._id); + } + + findById(id: string): Promise<TReward> { + return this.models.reward.findById(id); + } + + async createPayment({ reward, account }: { reward: TRewardNFT; account: TAccount }) { + const couponCode = await CouponCode.findOne({ couponRewardId: reward._id, sub: { $exists: false } }); + if (!couponCode) return { result: false, reason: 'No more coupon codes available' }; + + // Change owner of couponCode + await couponCode.updateOne({ sub: account.sub }); + + // Register payment + await this.models.payment.create({ + couponCodeId: couponCode._id, + rewardId: reward.id, + sub: account.sub, + poolId: reward.poolId, + amount: reward.pointPrice, + }); + } +} diff --git a/apps/api/src/app/services/RewardCustomService.ts b/apps/api/src/app/services/RewardCustomService.ts new file mode 100644 index 000000000..5937849e2 --- /dev/null +++ b/apps/api/src/app/services/RewardCustomService.ts @@ -0,0 +1,68 @@ +import { Identity, RewardCustom, RewardCustomPayment, Webhook } from '../models'; +import { IRewardService } from './interfaces/IRewardService'; +import { Event } from '@thxnetwork/common/enums'; +import WebhookService from './WebhookService'; + +export default class RewardCustomService implements IRewardService { + models = { + reward: RewardCustom, + payment: RewardCustomPayment, + }; + + async decorate({ reward, account }) { + const identities = account ? await Identity.find({ poolId: reward.poolId, sub: account.sub }) : []; + return { ...reward.toJSON(), isDisabled: !identities.length }; + } + + async decoratePayment(payment: TRewardPayment): Promise<TRewardPayment> { + return payment; + } + + async getValidationResult({ reward, account }: { reward: TReward; account?: TAccount }) { + const identities = account ? await Identity.find({ poolId: reward.poolId, sub: account.sub }) : []; + if (!identities.length) return { result: false, reason: 'No identity connected for this campaign.' }; + + return { result: true, reason: '' }; + } + + create(data: Partial<TReward>) { + return this.models.reward.create(data); + } + + update(reward: TReward, updates: Partial<TReward>): Promise<TReward> { + return this.models.reward.findByIdAndUpdate(reward._id, updates, { new: true }); + } + + remove(reward: TReward): Promise<void> { + return this.models.reward.findByIdAndDelete(reward._id); + } + + findById(id: string): Promise<TReward> { + return this.models.reward.findById(id); + } + + async createPayment({ + reward, + account, + }: { + reward: TReward; + account: TAccount; + }): Promise<TValidationResult | void> { + const webhook = await Webhook.findById(reward.webhookId); + if (!webhook) return { result: false, reason: 'Webhook not found.' }; + + // Call the webhook with known account identities for this campaign and optional metadata + await WebhookService.requestAsync(webhook, account.sub, { + type: Event.RewardCustomPayment, + data: { customRewardId: reward._id, metadata: reward.metadata }, + }); + + // Register the payment + await this.models.payment.create({ + rewardId: reward.id, + poolId: reward.poolId, + sub: account.sub, + amount: reward.pointPrice, + }); + } +} diff --git a/apps/api/src/app/services/RewardDiscordRoleService.ts b/apps/api/src/app/services/RewardDiscordRoleService.ts new file mode 100644 index 000000000..1fe5297b7 --- /dev/null +++ b/apps/api/src/app/services/RewardDiscordRoleService.ts @@ -0,0 +1,95 @@ +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import { RewardDiscordRole, RewardDiscordRolePayment } from '../models'; +import { IRewardService } from './interfaces/IRewardService'; +import { discordColorToHex } from '../util/discord'; +import DiscordService from './DiscordService'; + +export default class RewardDiscordRoleService implements IRewardService { + models = { + reward: RewardDiscordRole, + payment: RewardDiscordRolePayment, + }; + + async decorate({ reward, account }) { + const token = account && account.tokens.find(({ kind }) => kind === AccessTokenKind.Discord); + return { ...reward.toJSON(), isDisabled: !token }; + } + + async decoratePayment(payment: TRewardPayment): Promise<TRewardDiscordRolePayment> { + const reward = await this.models.reward.findById(payment.rewardId); + const guild = reward && (await DiscordService.getGuild(reward.poolId)); + const role = guild && reward && (await DiscordService.getRole(guild.id, reward.discordRoleId)); + const discordServerURL = guild && `https://discordapp.com/channels/${guild.id}/`; + + return { + ...payment.toJSON(), + discordServerURL, + guild: guild && { + name: guild.name, + icon: guild.icon && `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`, + }, + role: role && { + name: role.name, + color: discordColorToHex(role.color), + }, + }; + } + + async getValidationResult({ reward, account }: { reward: TReward; account?: TAccount }) { + const token = this.getToken(account); + if (!token) { + return { result: false, reason: 'Your account is not connected to a Discord account' }; + } + + const guild = await DiscordService.getGuild(reward.poolId); + if (!guild) { + return { result: false, reason: `THX Bot is not invited to the ${guild.name} Discord server` }; + } + + const member = await DiscordService.getMember(guild.id, token.userId); + if (!member) { + return { result: false, reason: `You are not a member of the ${guild.name} Discord server` }; + } + + return { result: true, reason: '' }; + } + + create(data: Partial<TReward>) { + return this.models.reward.create(data); + } + + update(reward: TReward, updates: Partial<TReward>): Promise<TReward> { + return this.models.reward.findByIdAndUpdate(reward, updates, { new: true }); + } + + remove(reward: TReward): Promise<void> { + return this.models.reward.findByIdAndDelete(reward._id); + } + + findById(id: string) { + return this.models.reward.findById(id); + } + + async createPayment({ reward, account }: { reward: TRewardNFT; account: TAccount }) { + const token = this.getToken(account); + const guild = await DiscordService.getGuild(reward.poolId); + const role = await DiscordService.getRole(guild.id, reward.discordRoleId); + const member = await DiscordService.getMember(guild.id, token.userId); + + // Add role to discord user + await member.roles.add(role); + + // Register the payment + await this.models.payment.create({ + rewardId: reward._id, + discordRoleId: reward.discordRoleId, + sub: account.sub, + poolId: reward.poolId, + amount: reward.pointPrice, + }); + } + + private getToken(account: TAccount) { + return account.tokens.find(({ kind }) => kind === AccessTokenKind.Discord); + } +} diff --git a/apps/api/src/app/services/RewardGalachainService.ts b/apps/api/src/app/services/RewardGalachainService.ts new file mode 100644 index 000000000..11df2f49e --- /dev/null +++ b/apps/api/src/app/services/RewardGalachainService.ts @@ -0,0 +1,156 @@ +import { RewardGalachain, RewardGalachainPayment, WalletDocument } from '../models'; +import GalachainService from './GalachainService'; +import { IRewardService } from './interfaces/IRewardService'; +import { BigNumber } from 'bignumber.js'; +import PoolService from './PoolService'; + +export default class RewardGalachainService implements IRewardService { + models = { + reward: RewardGalachain, + payment: RewardGalachainPayment, + }; + + async decorate({ reward }: { reward: TRewardGalachain; account?: TAccount }): Promise<any> { + const contract = { + channelName: reward.contractChannelName, + chaincodeName: reward.contractChaincodeName, + contractName: reward.contractContractName, + }; + const token = { + collection: reward.tokenCollection, + category: reward.tokenCategory, + type: reward.tokenType, + additionalKey: reward.tokenAdditionalKey, + instance: new BigNumber(0), + }; + const pool = await PoolService.getById(reward.poolId); + const [balance] = (await GalachainService.balanceOf( + contract, + token, + pool.settings.galachainPrivateKey, + )) as any[]; + const paymentCount = await this.models.payment.countDocuments({ + rewardId: reward._id, + }); + const progress = { + count: paymentCount, + limit: Number(balance.quantity) + paymentCount, + }; + + return { ...reward.toJSON(), progress, limit: balance.quantity }; + } + + async decoratePayment(payment: TRewardPayment): Promise<TRewardGalachainPayment> { + const reward = await this.models.reward.findById(payment.rewardId); + return { reward, ...payment.toJSON() }; + } + + async getValidationResult({ reward }: { reward: any; account?: TAccount }): Promise<TValidationResult> { + const { tokenCollection, tokenCategory, tokenType, tokenAdditionalKey } = reward; + const token = { + collection: tokenCollection, + category: tokenCategory, + type: tokenType, + additionalKey: tokenAdditionalKey, + instance: new BigNumber(0), + }; + const contract = { + channelName: reward.contractChannelName, + chaincodeName: reward.contractChaincodeName, + contractName: reward.contractContractName, + methodName: 'TransferToken', + }; + const pool = await PoolService.getById(reward.poolId); + + // Check balance of the distributor + const [balance] = (await GalachainService.balanceOf( + contract, + token, + pool.settings.galachainPrivateKey, + )) as any[]; + + if (Number(balance.quantity) < reward.amount) { + return { result: false, reason: 'Distributor has an insufficient balance' }; + } + + return { result: true, reason: '' }; + } + + create(data: any): Promise<any> { + return this.models.reward.create(data); + } + + update(reward: TReward, updates: Partial<TReward>): Promise<TReward> { + return this.models.reward.findByIdAndUpdate(reward, updates, { new: true }); + } + + remove(reward: TReward): Promise<void> { + return this.models.reward.findByIdAndDelete(reward._id); + } + + findById(id: string) { + return this.models.reward.findById(id); + } + + async createPayment({ + reward, + wallet, + }: { + reward: TRewardGalachain; + account: TAccount; + safe: WalletDocument; + wallet?: WalletDocument; + }): Promise<void | TValidationResult> { + const token = this.getToken(reward); + const contract = this.getContract(reward); + const pool = await PoolService.getById(reward.poolId); + + // Get first token from balance + const instanceId = await this.getInstance(contract, token, pool); + + // Transfer token to user wallet + await GalachainService.transfer( + contract, + token, + wallet.address, + Number(reward.amount), + instanceId, + pool.settings.galachainPrivateKey, + ); + + // Register payment + await this.models.payment.create({ + sub: wallet.sub, + walletId: wallet._id, + rewardId: reward._id, + amount: reward.amount, + }); + } + + private async getInstance(contract: TGalachainContract, token: TGalachainToken, pool: TPool) { + const [balance] = (await GalachainService.balanceOf( + contract, + token, + pool.settings.galachainPrivateKey, + )) as any[]; + const [instanceId] = balance.instanceIds; + return instanceId; + } + + private getToken(reward: TRewardGalachain) { + return { + collection: reward.tokenCollection, + category: reward.tokenCategory, + type: reward.tokenType, + additionalKey: reward.tokenAdditionalKey, + }; + } + + private getContract(reward: TRewardGalachain) { + return { + channelName: reward.contractChannelName, + chaincodeName: reward.contractChaincodeName, + contractName: reward.contractContractName, + }; + } +} diff --git a/apps/api/src/app/services/RewardNFTService.ts b/apps/api/src/app/services/RewardNFTService.ts new file mode 100644 index 000000000..d6c64139a --- /dev/null +++ b/apps/api/src/app/services/RewardNFTService.ts @@ -0,0 +1,179 @@ +import { + ERC1155MetadataDocument, + ERC1155TokenDocument, + ERC721MetadataDocument, + ERC721TokenDocument, + RewardNFT, + RewardNFTDocument, + RewardNFTPayment, + WalletDocument, +} from '@thxnetwork/api/models'; +import { NFTVariant } from '@thxnetwork/common/enums'; +import { IRewardService } from './interfaces/IRewardService'; +import ERC721Service from './ERC721Service'; +import ERC1155Service from './ERC1155Service'; +import PoolService from './PoolService'; +import SafeService from './SafeService'; + +export default class RewardNFTService implements IRewardService { + models = { + reward: RewardNFT, + payment: RewardNFTPayment, + }; + services = { + [NFTVariant.ERC721]: ERC721Service, + [NFTVariant.ERC1155]: ERC1155Service, + }; + + async decorate({ reward, account }: { reward: TRewardNFT; account?: TAccount }) { + const nft = await this.findNFT(reward); + const token = reward.tokenId && (await this.findTokenById(nft, reward.tokenId)); + const metadataId = token ? token.metadataId : reward.metadataId ? reward.metadataId : null; + const metadata = metadataId ? await this.findMetadataById(nft, metadataId) : null; + const expiry = reward.expiryDate && { + date: reward.expiryDate, + now: new Date(), + }; + return { ...reward.toJSON(), chainId: nft.chainId, nft, metadata, token, expiry }; + } + + async decoratePayment(payment: TRewardPayment): Promise<TRewardPayment> { + return payment; + } + + async getValidationResult({ + reward, + safe, + wallet, + }: { + reward: TRewardNFT; + safe?: WalletDocument; + wallet?: WalletDocument; + account?: TAccount; + }) { + const nft = await this.findNFT(reward); + if (!nft) return { result: false, reason: 'NFT contract is no longer available' }; + + // This will require a transfer + if (reward.tokenId) { + // Check if Safe is the owner + const { contract } = nft; + const token = await this.findTokenById(nft, reward.tokenId); + if (!token) return { result: false, reason: 'Token not found' }; + + const owner = await contract.methods.ownerOf(token.tokenId).call(); + if (owner.toLowerCase() !== safe.address.toLowerCase()) { + return { result: false, reason: 'Token is no longer owner by campaign Safe.' }; + } + } + + // Will require a mint + if (reward.metadataId) { + const isMinter = await this.services[nft.variant].isMinter(nft, safe.address); + if (!isMinter) return { result: false, reason: 'Campaign Safe is not a minter of the NFT contract.' }; + } + + // Check receiving wallet for chain compatibility + if (wallet.chainId !== nft.chainId) { + return { result: false, reason: 'Your wallet is not on the same chain as the NFT contract.' }; + } + + return { result: true, reason: '' }; + } + + async create(data: Partial<TRewardNFT>) { + // If erc721Id or erc1155Id, check if campaign safe is minter + if (data.metadataId) { + const pool = await PoolService.getById(data.poolId); + const safe = await SafeService.findOneByPool(pool, pool.chainId); + await this.addMinter(data, safe.address); + } + + return await this.models.reward.create(data); + } + + update(reward: TRewardNFT, updates: Partial<TRewardNFT>) { + return this.models.reward.findByIdAndUpdate(reward._id, updates, { new: true }); + } + + async remove(reward: RewardNFTDocument) { + await this.models.reward.findOneAndDelete(reward._id); + } + + async createPayment({ + reward, + safe, + wallet, + }: { + reward: RewardNFTDocument; + safe: WalletDocument; + wallet?: WalletDocument; + }) { + const erc1155Amount = reward.erc1155Amount && String(reward.erc1155Amount); + const nft = await this.findNFT(reward); + if (!nft) throw new Error('NFT not found'); + + // Get token and metadata for either ERC721 or ERC1155 based contracts + // and mint if metadataId is present or transfer if tokenId is present + let token: ERC721TokenDocument | ERC1155TokenDocument, + metadata: ERC721MetadataDocument | ERC1155MetadataDocument; + + // Mint a token if metadataId is present + if (reward.metadataId) { + metadata = await this.findMetadataById(nft, reward.metadataId); + + // Mint the token to wallet address + token = await this.services[nft.variant].mint(safe, nft, wallet, metadata, erc1155Amount); + } + + // Transfer a token if tokenId is present + if (reward.tokenId) { + token = await this.findTokenById(nft, reward.tokenId); + metadata = await this.findMetadataByToken(nft, token); + + // Transfer the token from safe to wallet address + token = await this.services[nft.variant].transferFrom(nft, safe, wallet.address, token, erc1155Amount); + } + + // Register the payment + await RewardNFTPayment.create({ + rewardId: reward._id, + sub: wallet.sub, + walletId: wallet._id, + poolId: reward.poolId, + amount: reward.pointPrice, + }); + } + + findById(id: string) { + return this.models.reward.findById(id); + } + + findMetadataByToken(nft: TERC721 | TERC1155, token: TERC721Token | TERC1155Token) { + return this.services[nft.variant].findMetadataByToken(token); + } + + findTokenById(nft: TERC721 | TERC1155, tokenId: string) { + return this.services[nft.variant].findTokenById(tokenId); + } + + findMetadataById(nft: TERC721 | TERC1155, metadataId: string) { + return this.services[nft.variant].findMetadataById(metadataId); + } + + findNFT({ erc721Id, erc1155Id }: { erc721Id?: string; erc1155Id?: string }) { + if (erc721Id) { + return ERC721Service.findById(erc721Id); + } + + if (erc1155Id) { + return ERC1155Service.findById(erc1155Id); + } + } + + private async addMinter({ erc721Id, erc1155Id }: { erc721Id?: string; erc1155Id?: string }, address: string) { + const nft = await this.findNFT({ erc721Id, erc1155Id }); + const isMinter = await this.services[nft.variant].isMinter(nft, address); + if (!isMinter) await this.services[nft.variant].addMinter(nft, address); + } +} diff --git a/apps/api/src/app/services/RewardService.ts b/apps/api/src/app/services/RewardService.ts new file mode 100644 index 000000000..aa6560118 --- /dev/null +++ b/apps/api/src/app/services/RewardService.ts @@ -0,0 +1,292 @@ +import { Document } from 'mongoose'; +import { RewardVariant } from '@thxnetwork/common/enums'; +import { Participant, QRCodeEntry, WalletDocument } from '@thxnetwork/api/models'; +import { v4 } from 'uuid'; +import { logger } from '../util/logger'; +import { Job } from '@hokify/agenda'; +import RewardCoinService from './RewardCoinService'; +import LockService from './LockService'; +import AccountProxy from '../proxies/AccountProxy'; +import ParticipantService from './ParticipantService'; +import RewardNFTService from './RewardNFTService'; +import RewardCouponService from './RewardCouponService'; +import ImageService from './ImageService'; +import PointBalanceService from './PointBalanceService'; +import MailService from './MailService'; +import RewardDiscordRoleService from './RewardDiscordRoleService'; +import RewardCustomService from './RewardCustomService'; +import RewardGalachainService from './RewardGalachainService'; +import PoolService from './PoolService'; +import WalletService from './WalletService'; +import THXService from './THXService'; + +const serviceMap = { + [RewardVariant.Coin]: new RewardCoinService(), + [RewardVariant.NFT]: new RewardNFTService(), + [RewardVariant.Custom]: new RewardCustomService(), + [RewardVariant.Coupon]: new RewardCouponService(), + [RewardVariant.DiscordRole]: new RewardDiscordRoleService(), + [RewardVariant.Galachain]: new RewardGalachainService(), +}; + +export default class RewardService { + static async count({ poolId }) { + const variants = Object.keys(RewardVariant).filter((v) => !isNaN(Number(v))); + const counts = await Promise.all( + variants.map(async (variant: string) => { + const Reward = serviceMap[variant].models.reward; + return await Reward.countDocuments({ poolId, isPublished: true }); + }), + ); + return counts.reduce((acc, count) => acc + count, 0); + } + + static async list({ pool, account }) { + const rewardVariants = Object.keys(RewardVariant).filter((v) => !isNaN(Number(v))); + const callback: any = async (variant: RewardVariant) => { + const Reward = serviceMap[variant].models.reward; + // Filter out rewards that have QR codes (RDM) + const qrCodeRewardIds = await QRCodeEntry.find().distinct('rewardId'); + const rewards = await Reward.find({ + _id: { $nin: qrCodeRewardIds }, + poolId: pool._id, + variant, + isPublished: true, + $or: [ + // Include quests with expiryDate less than or equal to now + { expiryDate: { $exists: true, $gte: new Date() } }, + // Include quests with no expiryDate + { expiryDate: { $exists: false } }, + ], + }); + return await Promise.all( + rewards.map(async (reward) => { + try { + const decorated = await serviceMap[reward.variant].decorate({ reward, account }); + const isLocked = await this.isLocked({ reward, account }); + const isStocked = await this.isStocked(reward); + const isExpired = this.isExpired(reward); + const isAvailable = await this.isAvailable({ reward, account }); + const progress = { + count: await serviceMap[reward.variant].models.payment.countDocuments({ + rewardId: reward._id, + }), + limit: reward.limit, + }; + + // Decorated properties may override generic properties + return { progress, isLocked, isStocked, isExpired, isAvailable, ...decorated }; + } catch (error) { + logger.error(error); + } + }), + ); + }; + + return await Promise.all(rewardVariants.map(callback)); + } + + static async findPaymentsBySub( + reward: TReward, + { skip, limit, query }: { skip: number; limit: number; query: string }, + ) { + const Payment = serviceMap[reward.variant].models.payment; + // Get all matching accounts by email and username first + const accounts = await AccountProxy.find({ query }); + // We then fetch the payments for the list of subs + const subs = accounts.map(({ sub }) => sub); + // Then we fetch the participants for the poolId and the list of subs + const participants = await Participant.find({ poolId: reward.poolId, sub: { $in: subs } }); + const payments = await Payment.find({ rewardId: reward._id, sub: { $in: subs } }) + .limit(limit) + .skip(skip); + + return { payments, accounts, participants }; + } + + static async findPaymentsByReward( + reward: TReward, + { skip, limit }: { skip: number; limit: number; query: string }, + ) { + const Payment = serviceMap[reward.variant].models.payment; + // If there is no query we fetch the payments for the reward + const payments = await Payment.find({ rewardId: reward._id }).limit(limit).skip(skip); + const subs = payments.map(({ sub }) => sub); + const accounts = await AccountProxy.find({ subs }); + const participants = await Participant.find({ poolId: reward.poolId, sub: { $in: subs } }); + + return { payments, accounts, participants }; + } + + static async findPayments(reward: TReward, { page, limit, query }: { page: number; limit: number; query: string }) { + const skip = (page - 1) * limit; + const Payment = serviceMap[reward.variant].models.payment; + const total = await Payment.countDocuments({ rewardId: reward._id }); + + // If there is a query we fetch accounts by username first + const { payments, accounts, participants } = + query.length > 3 + ? await this.findPaymentsBySub(reward, { skip, limit, query }) + : await this.findPaymentsByReward(reward, { skip, limit, query }); + const promises = payments.map(async (payment: Document & TRewardPayment) => + ParticipantService.decorate(payment, { accounts, participants }), + ); + const results = await Promise.allSettled(promises); + + return { + total, + limit, + page, + results: results.filter((result) => result.status === 'fulfilled').map((result: any) => result.value), + }; + } + + static async findPaymentsForSub(sub: string) { + const rewardVariants: string[] = Object.keys(RewardVariant).filter((v) => !isNaN(Number(v))); + const payments = await Promise.allSettled( + rewardVariants.map(async (variant: string) => { + const rewardVariant = Number(variant); + const payments = await serviceMap[rewardVariant].models.payment.find({ sub }); + const callback = payments.map(async (p: Document & TRewardPayment) => { + const decorated = await serviceMap[rewardVariant].decoratePayment(p); + return { ...decorated, rewardVariant }; + }); + return await Promise.all(callback); + }), + ); + + return payments + .filter((result) => result.status === 'fulfilled') + .map((result: any) => result.value) + .flat(); + } + + static async createPaymentJob(job: Job) { + try { + const { variant, sub, rewardId, walletId } = job.attrs.data as any; + const account = await AccountProxy.findById(sub); + const reward = await this.findById(variant, rewardId); + const pool = await PoolService.getById(reward.poolId); + const wallet = walletId && (await WalletService.findById(walletId)); + + // Validate supply, expiry, locked and reward specific validation + const validationResult = await this.getValidationResult({ reward, account, safe: pool.safe }); + if (!validationResult.result) return validationResult.reason; + + // Subtract points for account + await PointBalanceService.subtract(pool, account, reward.pointPrice); + + // Send email notification + let html = `<p style="font-size: 18px">Congratulations!🚀</p>`; + html += `<p>Your payment has been received! <strong>${reward.title}</strong> is available in your account.</p>`; + html += `<p class="btn"><a href="${pool.campaignURL}">View Wallet</a></p>`; + await MailService.send(account.email, `🎁 Reward Received!`, html); + + const payment = await serviceMap[variant].createPayment({ reward, account, safe: pool.safe, wallet }); + + // Register THX onboarding campaign event + await THXService.createEvent(account, 'reward_payment_created'); + + // Register the payment for the account + return payment; + } catch (error) { + console.log(error); + logger.error(error); + } + } + + static async create(variant: RewardVariant, poolId: string, data: Partial<TReward>, file: Express.Multer.File) { + if (file) { + data.image = await ImageService.upload(file); + } + + const reward = await serviceMap[variant].create({ ...data, poolId, variant, uuid: v4() }); + + // TODO Implement publish notification flow for rewards + // if (data.isPublished) { + // await NotificationService.notify(variant, quest); + // } + + return reward; + } + + static async update(reward: TReward, updates: Partial<TReward>, file?: Express.Multer.File) { + if (file) { + updates.image = await ImageService.upload(file); + } + + reward = await serviceMap[reward.variant].update(reward, updates); + + // TODO Implement publish notification flow for rewards + // if (data.isPublished) { + // await NotificationService.notify(variant, quest); + // } + + return reward; + } + + static async remove(reward: TReward) { + return await serviceMap[reward.variant].remove(reward); + } + + static findById(variant: RewardVariant, rewardId: string) { + return serviceMap[variant].findById(rewardId); + } + + static async getValidationResult({ + reward, + account, + safe, + wallet, + }: { + reward: TReward; + account?: TAccount; + safe?: WalletDocument; + wallet?: WalletDocument; + }) { + const participant = await Participant.findOne({ sub: account.sub, poolId: reward.poolId }); + if (Number(participant.balance) < Number(reward.pointPrice)) { + return { result: false, reason: 'Participant has insufficient points.' }; + } + + const isLocked = await this.isLocked({ reward, account }); + if (isLocked) return { result: false, reason: 'This reward is locked.' }; + + const isExpired = this.isExpired(reward); + if (isExpired) return { result: false, reason: 'This reward claim has expired.' }; + + const isStocked = await this.isStocked(reward); + if (!isStocked) return { result: false, reason: 'This reward is out of stock.' }; + + return serviceMap[reward.variant].getValidationResult({ reward, account, wallet, safe }); + } + + static async isLocked({ reward, account }) { + if (!account || !reward.locks.length) return false; + return await LockService.getIsLocked(reward.locks, account); + } + + static isExpired(reward: TReward) { + if (!reward.expiryDate) return false; + return Date.now() > new Date(reward.expiryDate).getTime(); + } + + static async isStocked(reward) { + if (!reward.limit) return true; + // Check if reward has a limit and if limit has been reached + const amountOfPayments = await serviceMap[reward.variant].models.payment.countDocuments({ + rewardId: reward._id, + }); + return amountOfPayments < reward.limit; + } + + static async isAvailable({ reward, account }: { reward: TReward; account?: TAccount }) { + if (!account) return true; + + const isLocked = await this.isLocked({ reward, account }); + const isStocked = await this.isStocked(reward); + const isExpired = this.isExpired(reward); + + return !isLocked && !isExpired && isStocked; + } +} diff --git a/apps/api/src/app/services/SafeService.ts b/apps/api/src/app/services/SafeService.ts new file mode 100644 index 000000000..608b2476e --- /dev/null +++ b/apps/api/src/app/services/SafeService.ts @@ -0,0 +1,254 @@ +import { Wallet, WalletDocument, Pool, PoolDocument, Transaction } from '@thxnetwork/api/models'; +import { ChainId, WalletVariant } from '@thxnetwork/common/enums'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { contractNetworks } from '@thxnetwork/api/hardhat'; +import ContractService, { safeVersion } from '@thxnetwork/api/services/ContractService'; +import { toChecksumAddress } from 'web3-utils'; +import Safe, { SafeAccountConfig, SafeFactory } from '@safe-global/protocol-kit'; +import SafeApiKit from '@safe-global/api-kit'; +import { + SafeMultisigTransactionResponse, + SafeTransactionData, + SafeTransactionDataPartial, + SafeVersion, +} from '@safe-global/safe-core-sdk-types'; +import { logger } from '@thxnetwork/api/util/logger'; +import { agenda, JobType } from '@thxnetwork/api/util/agenda'; +import { Job } from '@hokify/agenda'; +import { convertObjectIdToNumber } from '../util'; +import TransactionService from './TransactionService'; + +function getSafeApiKit(chainId: ChainId) { + const { txServiceUrl, ethAdapter } = getProvider(chainId); + return new SafeApiKit({ txServiceUrl, ethAdapter }); +} + +function reset(wallet: WalletDocument, userWalletAddress: string) { + const { defaultAccount } = getProvider(wallet.chainId); + return deploy(wallet, [toChecksumAddress(defaultAccount), toChecksumAddress(userWalletAddress)]); +} + +async function create( + data: { chainId: ChainId; sub: string; safeVersion?: SafeVersion; address?: string; poolId?: string }, + userWalletAddress?: string, +) { + const { safeVersion, chainId, sub, address, poolId } = data; + const { defaultAccount } = getProvider(chainId); + const wallet = await Wallet.create({ variant: WalletVariant.Safe, sub, chainId, address, safeVersion, poolId }); + + // Concerns a Metamask account so we do not deploy and return early + if (!safeVersion && address) return wallet; + + // Add relayer address and consider this a campaign safe + const owners = [toChecksumAddress(defaultAccount)]; + // Add user address as a signer and consider this a participant safe + if (userWalletAddress) owners.push(toChecksumAddress(userWalletAddress)); + + // If campaign safe we provide a nonce based on the timestamp in the MongoID the pool (poolId value) + const nonce = wallet.poolId && String(convertObjectIdToNumber(wallet.poolId)); + + return await deploy(wallet, owners, nonce); +} + +async function deploy(wallet: WalletDocument, owners: string[], nonce?: string) { + const { ethAdapter } = getProvider(wallet.chainId); + const safeFactory = await SafeFactory.create({ + safeVersion: wallet.safeVersion as SafeVersion, + ethAdapter, + contractNetworks, + }); + const safeAccountConfig: SafeAccountConfig = { + owners, + threshold: owners.length, + }; + const safeAddress = toChecksumAddress(await safeFactory.predictSafeAddress(safeAccountConfig, nonce)); + + try { + await Safe.create({ + ethAdapter, + safeAddress, + contractNetworks, + }); + } catch (error) { + await agenda.now(JobType.DeploySafe, { + safeAccountConfig, + safeVersion: wallet.safeVersion, + safeAddress, + safeWalletId: String(wallet._id), + }); + } + + return await Wallet.findByIdAndUpdate(wallet._id, { address: safeAddress }, { new: true }); +} + +async function createJob(job: Job) { + const { safeAccountConfig, safeVersion, safeAddress, safeWalletId } = job.attrs.data as any; + if (!safeAccountConfig || !safeVersion || !safeAddress || !safeWalletId) return; + + const wallet = await Wallet.findById(safeWalletId); + const { ethAdapter } = getProvider(wallet.chainId); + const safeFactory = await SafeFactory.create({ + safeVersion, + ethAdapter, + contractNetworks, + }); + + // If campaign safe we provide a nonce based on the timestamp in the MongoID the pool (poolId value) + const nonce = wallet.poolId && String(convertObjectIdToNumber(wallet.poolId)); + const config = { safeAccountConfig, options: { gasLimit: '3000000' } }; + if (nonce) config['saltNonce'] = nonce; + + await safeFactory.deploySafe(config); + logger.debug(`[${wallet.sub}] Deployed Safe: ${safeAddress}`); + + // Set safeAddress for campaign to keep address available for potential regression + if (wallet.poolId) { + await Pool.findByIdAndUpdate(wallet.poolId, { safeAddress: toChecksumAddress(safeAddress) }); + } +} + +function findById(id: string) { + return Wallet.findById(id); +} + +function findOne(query) { + return Wallet.findOne({ ...query, variant: WalletVariant.Safe, poolId: { $exists: false } }); +} + +function findOneByAddress(address: string) { + return Wallet.findOne({ address: toChecksumAddress(address) }); +} + +async function findOneByPool(pool: PoolDocument, chainId?: ChainId) { + return await Wallet.findOne({ + poolId: pool.id, + chainId: chainId || ContractService.getChainId(), + sub: pool.sub, + safeVersion, + }); +} + +async function getOwners(wallet: WalletDocument) { + const { ethAdapter } = getProvider(wallet.chainId); + const safeSdk = await Safe.create({ + ethAdapter, + safeAddress: wallet.address, + contractNetworks, + }); + + return await safeSdk.getOwners(); +} + +async function createSwapOwnerTransaction(wallet: WalletDocument, oldOwnerAddress: string, newOwnerAddress: string) { + const { ethAdapter } = getProvider(wallet.chainId); + const safeSdk = await Safe.create({ + ethAdapter, + safeAddress: wallet.address, + contractNetworks, + }); + + return await safeSdk.createSwapOwnerTx({ oldOwnerAddress, newOwnerAddress }); +} + +async function proposeTransaction(wallet: WalletDocument, safeTransactionData: SafeTransactionDataPartial) { + const { ethAdapter, signer } = getProvider(wallet.chainId); + const safe = await Safe.create({ + ethAdapter, + safeAddress: wallet.address, + contractNetworks, + }); + + // Get nonce for this Safes transaction + const nonce = await safe.getNonce(); + const safeTransaction = await safe.createTransaction({ + safeTransactionData, + options: { nonce: nonce + 1 }, + }); + + // Create hash for this transaction + const safeTxHash = await safe.getTransactionHash(safeTransaction); + const signature = await safe.signTransactionHash(safeTxHash); + const apiKit = getSafeApiKit(wallet.chainId); + + logger.info({ safeTxHash, nonce }); + + try { + await apiKit.proposeTransaction({ + safeAddress: wallet.address, + safeTxHash, + safeTransactionData: safeTransaction.data as any, + senderAddress: toChecksumAddress(await signer.getAddress()), + senderSignature: signature.data, + }); + + logger.info(`Safe TX Proposed: ${safeTxHash}`); + return safeTxHash; + } catch (error) { + logger.error(error); + } +} + +async function confirmTransaction(wallet: WalletDocument, safeTxHash: string) { + const { ethAdapter } = getProvider(wallet.chainId); + const safe = await Safe.create({ + ethAdapter, + safeAddress: wallet.address, + contractNetworks, + }); + const signature = await safe.signTransactionHash(safeTxHash); + return await confirm(wallet, safeTxHash, signature.data); +} + +async function confirm(wallet: WalletDocument, safeTxHash: string, signatureData: string) { + const { txServiceUrl, ethAdapter } = getProvider(wallet.chainId); + const apiKit = new SafeApiKit({ ethAdapter, txServiceUrl }); + return await apiKit.confirmTransaction(safeTxHash, signatureData); +} + +async function executeTransaction(wallet: WalletDocument, safeTxHash: string) { + const { ethAdapter } = getProvider(wallet.chainId); + const apiKit = getSafeApiKit(wallet.chainId); + const safe = await Safe.create({ + ethAdapter, + safeAddress: wallet.address, + contractNetworks, + }); + const safeTransaction = await apiKit.getTransaction(safeTxHash); + const executeTxResponse = await safe.executeTransaction(safeTransaction as any); + const receipt = await executeTxResponse.transactionResponse?.wait(); + const tx = await Transaction.findOne({ safeTxHash }); + + await TransactionService.executeCallback(tx, receipt as any); + + return receipt; +} + +async function getLastPendingTransactions(wallet: WalletDocument) { + const apiKit = getSafeApiKit(wallet.chainId); + const { results }: any = await apiKit.getPendingTransactions(wallet.address); + + return results as unknown as SafeMultisigTransactionResponse[]; +} + +async function getTransaction(wallet: WalletDocument, safeTxHash: string): Promise<SafeMultisigTransactionResponse> { + const apiKit = getSafeApiKit(wallet.chainId); + return (await apiKit.getTransaction(safeTxHash)) as unknown as SafeMultisigTransactionResponse; +} + +export default { + reset, + findById, + createSwapOwnerTransaction, + proposeTransaction, + confirmTransaction, + confirm, + getLastPendingTransactions, + getOwners, + create, + createJob, + findOneByAddress, + findOne, + getTransaction, + executeTransaction, + findOneByPool, +}; diff --git a/apps/api/src/app/services/THXService.ts b/apps/api/src/app/services/THXService.ts new file mode 100644 index 000000000..cdc75e67b --- /dev/null +++ b/apps/api/src/app/services/THXService.ts @@ -0,0 +1,37 @@ +import { THXAPIClient } from '@thxnetwork/sdk/clients'; +import { THX_CLIENT_ID, THX_CLIENT_SECRET } from '../config/secrets'; +import { Identity } from '../models'; +import AccountProxy from '../proxies/AccountProxy'; + +class THXService { + thx!: THXAPIClient; + + constructor() { + if (THX_CLIENT_ID && THX_CLIENT_SECRET) { + this.thx = new THXAPIClient({ + clientId: THX_CLIENT_ID, + clientSecret: THX_CLIENT_SECRET, + }); + } + } + + async connect(account: TAccount) { + if (!this.thx) return; + + if (!account.identity) { + account.identity = await this.thx.identity.create(); + await AccountProxy.update(account.sub, { identity: account.identity }); + } + + await Identity.updateOne({ uuid: account.identity }, { sub: account.sub }); + } + + async createEvent(account: TAccount, event: string) { + if (!this.thx || !account.identity) return; + await this.thx.events.create({ identity: account.identity, event }); + } +} + +const THXServiceInstance = new THXService(); + +export default THXServiceInstance; diff --git a/apps/api/src/app/services/TransactionService.ts b/apps/api/src/app/services/TransactionService.ts new file mode 100644 index 000000000..701df1db5 --- /dev/null +++ b/apps/api/src/app/services/TransactionService.ts @@ -0,0 +1,366 @@ +import { getProvider } from '@thxnetwork/api/util/network'; +import { ChainId, TransactionState, TransactionType } from '@thxnetwork/common/enums'; +import { MINIMUM_GAS_LIMIT, RELAYER_SPEED } from '@thxnetwork/api/config/secrets'; +import { paginatedResults } from '@thxnetwork/api/util/pagination'; +import { toChecksumAddress } from 'web3-utils'; +import { poll } from '@thxnetwork/api/util/polling'; +import { deployCallback as erc20DeployCallback } from './ERC20Service'; +import { RelayerTransactionPayload } from '@openzeppelin/defender-relay-client'; +import { Contract } from 'web3-eth-contract'; +import { Transaction, TransactionDocument, WalletDocument } from '@thxnetwork/api/models'; +import { TransactionReceipt } from 'web3-core'; +import ERC721Service from './ERC721Service'; +import ERC1155Service from './ERC1155Service'; +import SafeService from './SafeService'; + +function getById(id: string) { + return Transaction.findById(id); +} + +async function sendValue(to: string, value: string, chainId: ChainId) { + const { web3, defaultAccount } = getProvider(chainId); + const from = defaultAccount; + const gas = '21000'; + + let tx = await Transaction.create({ + state: TransactionState.Queued, + chainId, + from, + to, + gas, + }); + + const receipt = await web3.eth.sendTransaction({ + from, + to, + value, + gas, + }); + + if (receipt.transactionHash) { + tx.transactionHash = receipt.transactionHash; + tx.state = TransactionState.Mined; + tx = await tx.save(); + } + + return { tx, receipt }; +} + +async function send(to: string, fn: any, chainId: ChainId) { + const { web3, defaultAccount } = getProvider(chainId); + const from = defaultAccount; + const data = fn.encodeABI(); + const estimate = await fn.estimateGas({ from }); + const gas = estimate < MINIMUM_GAS_LIMIT ? MINIMUM_GAS_LIMIT : estimate; + + return web3.eth.sendTransaction({ + from, + to, + data, + gas, + }); +} + +/** + * Creates a transaction in the db and either executes or schedules a web3 transaction. + * + * When the chain has a relayer configured the transaction is scheduled through it instead of directly executed. + * + * By setting the forceSync bool to true you can force the call to behave synchronously. It will poll for the transaction to be executed and only return after the transaction and its callback are executed. + * + * @param to Recipient + * @param fn Web3 contract method + * @param chainId Chainid to execute on + * @param forceSync Boolean to force synchronous execution, this waits for the transaction to be processed before returning. + * @param callback Callback configuration. + * @returns The transaction ID. This can be stored so the status of the transaction can be queried. + */ +async function sendAsync( + to: string | null, + fn: any, + chainId: ChainId, + forceSync = true, + callback?: TTransactionCallback, +) { + const { web3, relayer, defaultAccount } = getProvider(chainId); + const data = fn.encodeABI(); + + const estimate = await fn.estimateGas({ from: defaultAccount }); + const gas = estimate < MINIMUM_GAS_LIMIT ? MINIMUM_GAS_LIMIT : estimate; + + const tx = await Transaction.create({ + type: relayer && !forceSync ? TransactionType.Relayed : TransactionType.Default, + state: TransactionState.Queued, + from: defaultAccount, + to, + chainId, + callback, + }); + if (relayer) { + const args: RelayerTransactionPayload = { + data, + speed: RELAYER_SPEED, + gasLimit: gas, + }; + if (to) args.to = to; + + const defenderTx = await relayer.sendTransaction(args); + + Object.assign(tx, { + transactionId: defenderTx.transactionId, + transactionHash: defenderTx.hash, + state: TransactionState.Sent, + }); + + await tx.save(); + + if (forceSync) { + await poll( + async () => { + const transaction = await getById(tx._id); + return queryTransactionStatusReceipt(transaction); + }, + (state: TransactionState) => state === TransactionState.Sent, + 500, + ); + } + } else { + const receipt = await web3.eth.sendTransaction({ + from: defaultAccount, + to, + data, + gas: gas + 100000, // This was originally added for relayed transactions, not sure if still needed + }); + + await transactionMined(tx, receipt); + } + + // We return the id because the transaction might be out of date and the transaction is not used by callers anyway. + return String(tx._id); +} + +async function execSafeAsync(wallet: WalletDocument, tx: TransactionDocument) { + const { relayer } = getProvider(wallet.chainId); + const safeTransaction = await SafeService.getTransaction(wallet, tx.safeTxHash); + + // If there is no relayer for the network the safe executes immediately + if (!relayer) { + const receipt = await SafeService.executeTransaction(wallet, tx.safeTxHash); + await transactionMined(tx, receipt as any); + return; + } + + // If there is a relayer the transaction is sent to Defender and the job + // processor polls for the receipt and invokes callback + const defenderTx = await relayer.sendTransaction({ + to: safeTransaction.to, + data: safeTransaction.data, + gasLimit: safeTransaction.safeTxGas || '196000', + speed: RELAYER_SPEED, + }); + + await tx.updateOne({ + transactionId: defenderTx.transactionId, + transactionHash: defenderTx.hash, + state: TransactionState.Sent, + }); +} + +async function proposeSafeAsync( + wallet: WalletDocument, + to: string | null, + data: string, + callback?: TTransactionCallback, +) { + const { relayer, defaultAccount } = getProvider(wallet.chainId); + const safeTxHash = await SafeService.proposeTransaction(wallet, { + to, + data, + value: '0', + }); + if (!safeTxHash) throw new Error("Couldn't propose transaction."); + + await SafeService.confirmTransaction(wallet, safeTxHash); + + return await Transaction.create({ + type: relayer ? TransactionType.Relayed : TransactionType.Default, + state: TransactionState.Confirmed, + safeTxHash, + chainId: wallet.chainId, + walletId: String(wallet._id), + from: defaultAccount, + to, + callback, + }); +} + +async function sendSafeAsync(wallet: WalletDocument, to: string | null, fn: any, callback?: TTransactionCallback) { + const data = fn.encodeABI(); + return proposeSafeAsync(wallet, to, data, callback); +} + +async function deploy(abi: any, bytecode: any, arg: any[], chainId: ChainId) { + const { web3, defaultAccount } = getProvider(chainId); + const contract = new web3.eth.Contract(abi) as unknown as Contract; + const gas = await contract + .deploy({ + data: bytecode, + arguments: arg, + }) + .estimateGas(); + const data = contract + .deploy({ + data: bytecode, + arguments: arg, + }) + .encodeABI(); + + const tx = await Transaction.create({ + type: TransactionType.Default, + state: TransactionState.Queued, + from: defaultAccount, + chainId, + gas, + }); + + const receipt = await web3.eth.sendTransaction({ + from: defaultAccount, + data, + gas, + }); + + if (receipt.transactionHash) { + await tx.updateOne({ + to: receipt.to, + transactionHash: receipt.transactionHash, + state: TransactionState.Mined, + }); + } + + contract.options.address = receipt.contractAddress; + + return contract; +} + +async function transactionMined(tx: TransactionDocument, receipt: TransactionReceipt) { + Object.assign(tx, { + transactionHash: receipt.transactionHash, + state: TransactionState.Failed, + }); + + if (receipt.to) { + Object.assign(tx, { to: toChecksumAddress(receipt.to) }); + } + + if (tx.callback) { + try { + await executeCallback(tx, receipt); + tx.state = TransactionState.Mined; + } catch (e) { + tx.failReason = e.message; + } + } + + await tx.save(); +} + +async function executeCallback(tx: TransactionDocument, receipt: TransactionReceipt) { + if (!tx || !tx.callback) return; + switch (tx.callback.type) { + case 'Erc20DeployCallback': + await erc20DeployCallback(tx.callback.args, receipt); + break; + case 'Erc721DeployCallback': + await ERC721Service.deployCallback(tx.callback.args, receipt); + break; + case 'ERC1155DeployCallback': + await ERC1155Service.deployCallback(tx.callback.args, receipt); + break; + case 'erc721TokenMintCallback': + await ERC721Service.mintCallback(tx.callback.args, receipt); + break; + case 'erc1155TokenMintCallback': + await ERC1155Service.mintCallback(tx.callback.args, receipt); + break; + case 'erc721nTransferFromCallback': + await ERC721Service.transferFromCallback(tx.callback.args, receipt); + break; + case 'erc1155TransferFromCallback': + await ERC1155Service.transferFromCallback(tx.callback.args, receipt); + break; + } +} + +async function queryTransactionStatusDefender(tx: TransactionDocument) { + if ([TransactionState.Mined, TransactionState.Failed].includes(tx.state)) { + return tx; + } + const { web3, relayer } = getProvider(tx.chainId); + + const defenderTx = await relayer.query(tx.transactionId); + + // Hash has been updated + if (tx.transactionHash != defenderTx.hash) { + tx.transactionHash = defenderTx.hash; + await tx.save(); + } + + if (['mined', 'confirmed'].includes(defenderTx.status)) { + const receipt = await web3.eth.getTransactionReceipt(tx.transactionHash); + await transactionMined(tx, receipt); + } else if (defenderTx.status === 'failed') { + tx.state = TransactionState.Failed; + await tx.save(); + } + + return tx.state; +} + +async function queryTransactionStatusReceipt(tx: TransactionDocument) { + if ([TransactionState.Mined, TransactionState.Failed].includes(tx.state)) { + return tx; + } + const { web3 } = getProvider(tx.chainId); + + const receipt = await web3.eth.getTransactionReceipt(tx.transactionHash); + + if (receipt) { + // Wait 500 ms for transactions to be propagated to all nodes. + // Since we use multiple RPCs it happens we already have the receipt but the other RPC + // doesn't have the block available yet. + await new Promise((done) => setTimeout(done, 500)); + + await transactionMined(tx, receipt); + } + + return tx.state; +} + +async function findByQuery(poolAddress: string, page = 1, limit = 10, startDate?: Date, endDate?: Date) { + const query: Record<string, any> = { to: poolAddress }; + + if (startDate || endDate) query.createdAt = {}; + if (startDate) { + query.createdAt['$gte'] = startDate; + } + if (endDate) { + query.createdAt['$lt'] = endDate; + } + + return paginatedResults(Transaction, page, limit, query); +} + +export default { + getById, + send, + sendAsync, + deploy, + sendValue, + findByQuery, + sendSafeAsync, + execSafeAsync, + queryTransactionStatusDefender, + queryTransactionStatusReceipt, + executeCallback, + proposeSafeAsync, +}; diff --git a/apps/api/src/app/services/TwitterCacheService.ts b/apps/api/src/app/services/TwitterCacheService.ts new file mode 100644 index 000000000..cdd79d4ee --- /dev/null +++ b/apps/api/src/app/services/TwitterCacheService.ts @@ -0,0 +1,264 @@ +import { AccessTokenKind, JobType, OAuthRequiredScopes, OAuthTwitterScope } from '@thxnetwork/common/enums'; +import { agenda } from '../util/agenda'; +import { Job, QuestSocial, TwitterLike, TwitterQueryDocument, TwitterRepost, TwitterUser } from '../models'; +import { AxiosResponse } from 'axios'; +import { logger } from '../util/logger'; +import AccountProxy from '../proxies/AccountProxy'; +import TwitterDataProxy from '../proxies/TwitterDataProxy'; +import { TwitterPost } from '../models/TwitterPost'; + +function findUserById(users: { id: string }[], userId: string) { + return users.find((user: { id: string }) => user.id === userId); +} + +export default class TwitterCacheService { + static savePosts(posts: TTwitterPostWithUserAndMedia[] = [], query?: TwitterQueryDocument) { + return Promise.all( + posts.map(async (post) => { + await this.savePost(post, post.media, query); + await this.saveUser(post.user); + }), + ); + } + + static savePost(post: TTwitterPostResponse, media: TTwitterMediaResponse[] = [], query?: TwitterQueryDocument) { + return TwitterPost.findOneAndUpdate( + { + postId: post.id, + queryId: query && query.id, + }, + { + postId: post.id, + queryId: query && query.id, + userId: post.author_id, + text: post.text, + media: media.map((m: TTwitterMediaResponse) => ({ + url: m.url, + type: m.type, + previewImageUrl: m.preview_image_url, + width: m.width, + height: m.height, + })), + }, + { upsert: true, new: true }, + ); + } + + static saveUser(user: TTwitterUserResponse) { + if (!user) return; + return TwitterUser.findOneAndUpdate( + { userId: user.id }, + { + userId: user.id, + profileImgUrl: user.profile_image_url, + name: user.name, + username: user.username, + publicMetrics: { + followersCount: user.public_metrics.followers_count, + followingCount: user.public_metrics.following_count, + tweetCount: user.public_metrics.tweet_count, + listedCount: user.public_metrics.listed_count, + likeCount: user.public_metrics.like_count, + }, + }, + { upsert: true, new: true }, + ); + } + + static async updatePostCache( + account: TAccount, + quest: TQuestSocial, + token: TToken, + params: TTwitterRequestParams = { max_results: 100 }, + ) { + try { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Post verification calls X API.`); + const data = await TwitterDataProxy.request(token, { + url: `/tweets/search/recent`, + method: 'GET', + params, + }); + logger.info(`Fetched ${data.meta.result_count} reposts from X.`); + + // If no results return early + if (!data.meta.result_count) return; + } catch (res) { + await this.handleRateLimitError(res, account, quest, params, JobType.UpdateTwitterRepostCache); + } + } + + static async updateRepostCache( + account: TAccount, + quest: TQuestSocial, + token: TToken, + params: TTwitterRequestParams = { max_results: 100 }, + ) { + const postId = quest.content; + try { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Repost verification calls X API.`); + const data = await TwitterDataProxy.request(token, { + url: `/tweets/${postId}/retweeted_by`, + method: 'GET', + params, + }); + logger.info(`Fetched ${data.meta.result_count} reposts from X.`); + + // If no results return early + if (!data.meta.result_count) return; + + // If not then we upsert all TwitterReposts into the database + const operations = data.data.map((user: { id: string }) => ({ + updateOne: { + filter: { userId: user.id, postId }, + update: { userId: user.id, postId }, + upsert: true, + }, + })); + await TwitterRepost.bulkWrite(operations); + + // If the user has reposted the post, we return early + if (findUserById(data.data, token.userId)) return; + + // If there is a next_token, we store the next_token in case we get rate limited + // and continue on the next page + if (data.meta.next_token) { + // Start with caching the next 100 results + await this.updateRepostCache(account, quest, token, { + ...params, + pagination_token: data.meta.next_token, + }); + } + } catch (res) { + await this.handleRateLimitError(res, account, quest, params, JobType.UpdateTwitterRepostCache); + } + } + + static async updateLikeCache( + account: TAccount, + quest: TQuestSocial, + token: TToken, + params: TTwitterRequestParams = { max_results: 100 }, + ) { + const postId = quest.content; + + try { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Like verification calls X API.`); + const data = await TwitterDataProxy.request(token, { + url: `/tweets/${postId}/liking_users`, + method: 'GET', + params, + }); + logger.info(`Fetched ${data.meta.result_count} likes from X.`); + + // If no results return early + if (!data.meta.result_count) return; + + // If not then we upsert all TwitterLikes into the database + const operations = data.data.map((user: { id: string }) => ({ + updateOne: { + filter: { userId: user.id, postId }, + update: { userId: user.id, postId }, + upsert: true, + }, + })); + await TwitterLike.bulkWrite(operations); + + // If the user has liked the post, we return early + if (findUserById(data.data, token.userId)) return; + + // If there is a next_token, we store the next_token in case we get rate limited + // and continue on the next page + if (data.meta.next_token) { + // Start with caching the next 100 results + await this.updateLikeCache(account, quest, token, { + ...params, + pagination_token: data.meta.next_token, + }); + } + } catch (res) { + await this.handleRateLimitError(res, account, quest, params, JobType.UpdateTwitterLikeCache); + } + } + + static async handleRateLimitError( + res: AxiosResponse, + account: TAccount, + quest: TQuestSocial, + params: TTwitterRequestParams, + jobType: JobType, + ) { + // Retrow the error if it's not a rate limit error + if (res.status === 429) { + const sub = account.sub; + const questId = String(quest._id); + const job = await Job.findOne({ + 'name': jobType, + 'data.sub': sub, + 'data.questId': questId, + }); + + if (!job) { + const resetTime = Number(res.headers['x-rate-limit-reset']); + const seconds = resetTime - Math.ceil(Date.now() / 1000); + const minutes = Math.ceil(seconds / 60); + + // Resume caching when rate limit is reset + await agenda.schedule(`in ${minutes} minutes`, jobType, { + sub, + questId, + params, + }); + + logger.info('Scheduled updateLikeCacheJob', { questId, sub, params }); + } + } + + throw res; + } + + static async updateRepostCacheJob(job: TJob) { + await this.updateCacheJob(job, OAuthRequiredScopes.TwitterValidateRepost, this.updateRepostCache.bind(this)); + } + + static async updateLikeCacheJob(job: TJob) { + await this.updateCacheJob(job, OAuthRequiredScopes.TwitterValidateLike, this.updateLikeCache.bind(this)); + } + + static async updateCacheJob( + job: TJob, + scopes: OAuthTwitterScope[], + updateCacheCallback: ( + account: TAccount, + quest: TQuestSocial, + token: TToken, + params: TTwitterRequestParams, + ) => Promise<void>, + ) { + const { questId, sub, params } = job.attrs.data as { + sub: string; + questId: string; + params: TTwitterRequestParams; + }; + logger.info(`Starting ${job.attrs.name}`, params); + + try { + const quest = await QuestSocial.findById(questId); + if (!quest) throw new Error(`No token found for questId ${questId}.`); + + const account = await AccountProxy.findById(sub); + if (!account) throw new Error(`No account found for sub ${sub}.`); + + const token = await AccountProxy.getToken(account, AccessTokenKind.Twitter, scopes); + if (!token) throw new Error(`No token found for sub ${sub}.`); + + // Remove this job so it can be recreated if another rate limit is hit + await job.remove(); + + // Continue cache update for likes or reposts until the last page is reached + // or the next rate limit is hit + await updateCacheCallback(account, quest, token, params); + } catch (error) { + logger.error(error.response ? error.response : error); + } + } +} diff --git a/apps/api/src/app/services/TwitterQueryService.ts b/apps/api/src/app/services/TwitterQueryService.ts new file mode 100644 index 000000000..c88aae3c7 --- /dev/null +++ b/apps/api/src/app/services/TwitterQueryService.ts @@ -0,0 +1,131 @@ +import QuestService from './QuestService'; +import TwitterDataProxy from '../proxies/TwitterDataProxy'; +import TwitterCacheService from './TwitterCacheService'; +import MailService from './MailService'; +import AccountProxy from '../proxies/AccountProxy'; +import { Pool, PoolDocument, QuestSocial, TwitterQuery, TwitterQueryDocument } from '../models'; +import { DASHBOARD_URL } from '../config/secrets'; +import { QuestSocialRequirement, QuestVariant } from '@thxnetwork/common/enums'; +import { logger } from '../util/logger'; +import { TwitterPost } from '../models/TwitterPost'; + +const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000; + +export default class TwitterQueryService { + static async searchJob() { + const queries = await TwitterQuery.find(); + await this.run(queries); + } + + static async list(query: { poolId: string }): Promise<TwitterQueryDocument[]> { + const queries = await TwitterQuery.find(query); + return await Promise.all( + queries.map(async (query) => { + const posts = await TwitterPost.find({ queryId: query.id }); + return { ...query.toJSON(), posts }; + }), + ); + } + + static async run(queries: TwitterQueryDocument[]) { + const poolIds = queries.map((query) => query.poolId); + const pools = await Pool.find({ _id: { $in: poolIds } }); + const subs = pools.map((pool) => pool.sub); + const accounts = await AccountProxy.find({ subs }); + + for (const query of queries) { + try { + const pool = pools.find((pool) => pool.id === query.poolId); + if (!pool) continue; + + const account = accounts.find((account) => account.sub === pool.sub); + if (!account) continue; + + const posts = await this.search(account, query); + if (!posts.length) continue; + + // Send notification to campaign owner if new quests are created + await this.sendMail(account, pool, posts); + } catch (error) { + logger.error(error); + } + } + } + + static async search(account: TAccount, query: TwitterQueryDocument) { + const posts = await TwitterDataProxy.search(account, query.query); + + // Filter out the posts that already have a quest + const postIds = posts.map((post) => post.id); + const quests = await QuestSocial.find({ poolId: query.poolId, content: { $in: postIds } }); + const postsWithoutQuest = posts.filter((post) => { + return !quests.some((quest) => quest.content === post.id); + }); + + // Iterate over posts and create quests + for (const post of postsWithoutQuest) { + await this.createQuest( + query, + await TwitterCacheService.savePost(post, post.media, query), + await TwitterCacheService.saveUser(post.user), + ); + } + + return postsWithoutQuest; + } + + static async sendMail(account: TAccount, pool: PoolDocument, posts: TTwitterPostWithUserAndMedia[]) { + const src = new URL(DASHBOARD_URL); + src.pathname = `/pool/${pool.id}/quests`; + src.searchParams.append('isPublished', 'false'); + + await MailService.send( + account.email, + '👀 New matches for your query!', + `<p>Hi!</p> + <p>We found matches for your X query!</p> + ${posts + .map( + (post) => + `<div><strong>${post.user.username}</strong> <span>(${ + post.public_metrics.impression_count + } views)</span></div> + <p>${post.text.substring(0, 100)}... <a href="https://x.com/${post.user.username}/status/${ + post.id + }" target="_blank"> + View Post + </a></p>`, + ) + .join('<br />')} + `, + { src: src.toString(), text: 'Publish Quests' }, + ); + } + + static async createQuest(query: TTwitterQuery, post: TTwitterPost, user: TTwitterUser) { + const file = null; // TODO Download buffer for the media first URL and upload with quest + const quest = { + kind: 'twitter', + interaction: QuestSocialRequirement.TwitterLikeRetweet, + title: 'Repost & Like', + description: query.defaults.description, + amount: query.defaults.amount, + locks: query.defaults.locks, + isPublished: query.defaults.isPublished, + content: post.postId, + contentMetadata: JSON.stringify({ + url: `https://twitter.com/${user.username.toLowerCase()}/status/${post.postId}`, + username: user.username, + name: user.name, + text: post.text, + minFollowersCount: query.defaults.minFollowersCount, + }), + }; + + if (query.defaults.expiryInDays > 0) { + quest['expiryDate'] = new Date(Date.now() + query.defaults.expiryInDays * ONE_DAY_IN_MS); + } + + await QuestService.create(QuestVariant.Twitter, query.poolId, quest, file); + } +} diff --git a/apps/api/src/app/services/VoteEscrowService.ts b/apps/api/src/app/services/VoteEscrowService.ts new file mode 100644 index 000000000..a8c8c67b9 --- /dev/null +++ b/apps/api/src/app/services/VoteEscrowService.ts @@ -0,0 +1,198 @@ +import { getProvider } from '@thxnetwork/api/util/network'; +import { contractArtifacts, contractNetworks } from '@thxnetwork/api/hardhat'; +import { ChainId } from '@thxnetwork/common/enums'; +import { WalletDocument } from '@thxnetwork/api/models'; +import { toChecksumAddress } from 'web3-utils'; +import TransactionService from '@thxnetwork/api/services/TransactionService'; +import { logger } from '../util/logger'; +import { NODE_ENV } from '../config/secrets'; + +async function isApprovedAddress(address: string, chainId: ChainId) { + const { web3 } = getProvider(chainId); + const whitelist = new web3.eth.Contract( + contractArtifacts['SmartWalletWhitelist'].abi, + contractNetworks[chainId].SmartWalletWhitelist, + ); + return await whitelist.methods.check(address).call(); +} + +async function list(wallet: WalletDocument) { + const { web3 } = getProvider(wallet.chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + return await ve.methods.locked(wallet.address).call(); +} + +async function getAllowance(wallet: WalletDocument, tokenAddress: string, spender: string) { + const { web3 } = getProvider(wallet.chainId); + const bpt = new web3.eth.Contract(contractArtifacts['BPT'].abi, tokenAddress); + return await bpt.methods.allowance(wallet.address, spender).call(); +} + +async function approve(wallet: WalletDocument, tokenAddress: string, spender: string, amount: string) { + const { web3 } = getProvider(wallet.chainId); + const bpt = new web3.eth.Contract(contractArtifacts['BPT'].abi, tokenAddress); + const fn = bpt.methods.approve(spender, amount); + + // Propose tx data to relayer and return safeTxHash to client to sign + return await TransactionService.sendSafeAsync(wallet, bpt.options.address, fn); +} + +async function increaseAmount(wallet: WalletDocument, amountInWei: string) { + const { web3 } = getProvider(wallet.chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + const fn = ve.methods.increase_amount(amountInWei); + return TransactionService.sendSafeAsync(wallet, contractNetworks[wallet.chainId].VotingEscrow, fn); +} + +async function increaseUnlockTime(wallet: WalletDocument, endTimestamp: number) { + const { web3 } = getProvider(wallet.chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + const fn = ve.methods.increase_unlock_time(endTimestamp); + return TransactionService.sendSafeAsync(wallet, contractNetworks[wallet.chainId].VotingEscrow, fn); +} + +async function deposit(wallet: WalletDocument, amountInWei: string, endTimestamp: number) { + const { web3 } = getProvider(wallet.chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + const fn = ve.methods.create_lock(amountInWei, endTimestamp); + return TransactionService.sendSafeAsync(wallet, contractNetworks[wallet.chainId].VotingEscrow, fn); +} + +async function withdraw(wallet: WalletDocument, isEarlyWithdraw: boolean) { + const { web3 } = getProvider(wallet.chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[wallet.chainId].VotingEscrow, + ); + + // Check for lock and determine ve function to call + const fn = isEarlyWithdraw ? ve.methods.withdraw_early() : ve.methods.withdraw(); + + // Propose tx data to relayer and return safeTxHash to client to sign + const tx = await TransactionService.sendSafeAsync(wallet, ve.options.address, fn); + + return [tx]; +} + +async function listRewards(wallet: WalletDocument) { + const { web3 } = getProvider(wallet.chainId); + + // Get reward tokens + const lr = new web3.eth.Contract(contractArtifacts['LensReward'].abi, contractNetworks[wallet.chainId].LensReward); + + // Call static + const rewardTokens = [contractNetworks[wallet.chainId].BAL, contractNetworks[wallet.chainId].BPT]; + const callStatic = async (fn) => { + const result = await web3.eth.call({ + to: contractNetworks[wallet.chainId].LensReward, + data: fn.encodeABI(), + from: toChecksumAddress(wallet.address), + }); + return web3.eth.abi.decodeParameters( + [ + { + type: 'tuple[]', + components: [ + { type: 'address', name: 'tokenAddress' }, + { type: 'uint256', name: 'amount' }, + ], + }, + ], + result, + ); + }; + + // Call static on rewards + const rewards = await callStatic( + lr.methods.getUserClaimableRewardsAll( + contractNetworks[wallet.chainId].RewardDistributor, + toChecksumAddress(wallet.address), + rewardTokens, + ), + ); + + // Util functions to get amount for tokenAddress from call + const getAmount = (tokenAddress: string) => { + return rewards['0'].find((r) => r.tokenAddress === tokenAddress)?.amount; + }; + const { BAL, BPT } = contractNetworks[wallet.chainId]; + + return [ + { + tokenAddress: BAL, + amount: getAmount(BAL), + symbol: 'BAL', + }, + { + tokenAddress: BPT, + amount: getAmount(BPT), + symbol: '20USDC-80THX', + }, + ]; +} + +async function claimTokens(wallet: WalletDocument) { + const { web3 } = getProvider(wallet.chainId); + const rewardDistributor = new web3.eth.Contract( + contractArtifacts['RewardDistributor'].abi, + contractNetworks[wallet.chainId].RewardDistributor, + ); + + // List reward tokens and build function call + const rewardTokens = await rewardDistributor.methods.getAllowedRewardTokens().call(); + const fn = rewardDistributor.methods.claimTokens(wallet.address, rewardTokens); + + // Propose tx data to relayer and return safeTxHash to client to sign + const tx = await TransactionService.sendSafeAsync(wallet, rewardDistributor.options.address, fn); + + return [tx]; +} + +async function claimExternalRewardsJob() { + for (const chainId of [ChainId.Hardhat, ChainId.Polygon]) { + try { + if (NODE_ENV === 'production' && chainId === ChainId.Hardhat) continue; + const { web3 } = getProvider(chainId); + const ve = new web3.eth.Contract( + contractArtifacts['VotingEscrow'].abi, + contractNetworks[chainId].VotingEscrow, + ); + + // Execute directly using the relayer + const receipt = await TransactionService.send( + ve.options.address, + ve.methods.claimExternalRewards(), + chainId, + ); + logger.info(`ClaimExternalRewards: ${receipt.transactionHash}`); + } catch (error) { + logger.error(`ClaimExternalRewards: ${error && error.message}`); + } + } +} + +export default { + claimExternalRewardsJob, + list, + isApprovedAddress, + approve, + getAllowance, + deposit, + withdraw, + listRewards, + claimTokens, + increaseAmount, + increaseUnlockTime, +}; diff --git a/apps/api/src/app/services/WalletService.ts b/apps/api/src/app/services/WalletService.ts new file mode 100644 index 000000000..b8ede9c58 --- /dev/null +++ b/apps/api/src/app/services/WalletService.ts @@ -0,0 +1,66 @@ +import { Wallet } from '@thxnetwork/api/models/Wallet'; +import { TransactionState, WalletVariant } from '@thxnetwork/common/enums'; +import { Transaction } from '@thxnetwork/api/models/Transaction'; +import ContractService, { safeVersion } from './ContractService'; +import SafeService from './SafeService'; + +export default class WalletService { + static findById(id: string) { + if (!id) return; + return Wallet.findById(id); + } + + static async list(account: TAccount): Promise<TWallet[]> { + // List all wallets owned by the account but filter out wallets used for the campaign + const wallets = await Wallet.find({ + sub: account.sub, + variant: { $in: [WalletVariant.Safe, WalletVariant.WalletConnect] }, + address: { $exists: true, $ne: null }, + poolId: { $exists: false }, + }); + + return await Promise.all( + wallets.map(async (wallet) => { + const pendingTransactions = await Transaction.find({ + walletId: String(wallet._id), + state: TransactionState.Confirmed, + }); + const short = wallet.address && WalletService.formatAddress(wallet.address); + + return { ...wallet.toJSON(), short, pendingTransactions }; + }), + ); + } + + static findOne(query: Partial<TWallet>) { + return Wallet.findOne(query); + } + + static formatAddress(address: string) { + return `${address.slice(0, 5)}...${address.slice(-3)}`; + } + + static create(variant: WalletVariant, data: Partial<TWallet>) { + const chainId = ContractService.getChainId(); + const map = { + [WalletVariant.Safe]: WalletService.createSafe, + [WalletVariant.WalletConnect]: WalletService.createWalletConnect, + }; + return map[variant]({ ...(data as TWallet), chainId }); + } + + static async createSafe({ sub, address, chainId }) { + const safeWallet = await SafeService.findOne({ sub }); + // An account can have max 1 Safe + if (safeWallet) throw new Error('Already has a Safe.'); + + // Deploy a Safe with Web3Auth address and relayer as signers + await SafeService.create({ sub, chainId, safeVersion }, address); + } + + static async createWalletConnect({ sub, address, chainId }) { + const data: Partial<TWallet> = { variant: WalletVariant.WalletConnect, sub, address, chainId }; + + await Wallet.findOneAndUpdate({ sub, address, chainId }, data, { upsert: true }); + } +} diff --git a/apps/api/src/app/services/WebhookService.ts b/apps/api/src/app/services/WebhookService.ts new file mode 100644 index 000000000..48cb7b940 --- /dev/null +++ b/apps/api/src/app/services/WebhookService.ts @@ -0,0 +1,94 @@ +import axios from 'axios'; +import { Pool } from '@thxnetwork/api/models'; +import { Webhook, WebhookDocument } from '@thxnetwork/api/models/Webhook'; +import { Identity } from '@thxnetwork/api/models/Identity'; +import { WebhookRequest, WebhookRequestDocument } from '@thxnetwork/api/models/WebhookRequest'; +import { Job } from '@hokify/agenda'; +import { agenda } from '@thxnetwork/api/util/agenda'; +import { signPayload } from '@thxnetwork/api/util/signingsecret'; +import { JobType, Event, WebhookRequestState } from '@thxnetwork/common/enums'; + +export default class WebhookService { + static async request(webhook: WebhookDocument, account: TAccount, metadata?: string) { + const identities = (await Identity.find({ poolId: webhook.poolId, sub: account.sub })).map((i) => i.uuid); + const webhookRequest = await WebhookRequest.create({ + webhookId: webhook._id, + payload: JSON.stringify({ type: 'quest_entry.create', identities, metadata }), + state: WebhookRequestState.Pending, + }); + + return await this.executeRequest(webhook, webhookRequest); + } + + static async requestAsync( + webhook: WebhookDocument, + sub: string, + payload: { type: Event; data: any & { metadata: any } }, + ) { + const identities = (await Identity.find({ poolId: webhook.poolId, sub })).map((i) => i.uuid); + const webhookRequest = await WebhookRequest.create({ + webhookId: webhook._id, + payload: JSON.stringify({ ...payload, identities }), + state: WebhookRequestState.Pending, + }); + + await agenda.now(JobType.RequestAttemp, { + webhookRequestId: String(webhookRequest._id), + poolId: webhook.poolId, + }); + } + + static async requestAttemptJob(job: Job) { + const { webhookRequestId } = job.attrs.data as any; + + const webhookRequest = await WebhookRequest.findById(webhookRequestId); + if (!webhookRequest) throw new Error('No webhook request object found'); + + const webhook = await Webhook.findById(webhookRequest.webhookId); + if (!webhook) throw new Error('No webhook object found'); + + await this.executeRequest(webhook, webhookRequest); + } + + static async executeRequest(webhook: WebhookDocument, webhookRequest: WebhookRequestDocument) { + try { + const pool = await Pool.findById(webhook.poolId); + if (!pool.signingSecret) throw new Error('No signing secret found'); + + const signature = signPayload(webhookRequest.payload, pool.signingSecret); + webhookRequest.state = WebhookRequestState.Sent; + + const response = await axios({ + method: 'POST', + url: webhook.url, + data: { signature, payload: webhookRequest.payload }, + headers: { + 'Content-Type': 'application/json', + }, + }); + + webhookRequest.state = WebhookRequestState.Received; + webhookRequest.httpStatus = response.status; + + console.debug(`[${response.status}], ${JSON.stringify(response.data)}`); + + return response && response.data; + } catch (error) { + console.log(error); + + webhookRequest.state = WebhookRequestState.Failed; + webhookRequest.failReason = error && error.toString(); + + // If there is an HTTP response we store the HTTP error and status code + if (error && error.response) { + webhookRequest.httpStatus = error.response.status; + webhookRequest.failReason = JSON.stringify(error.response.data); + } + + console.error(error); + } finally { + webhookRequest.attempts = webhookRequest.attempts++; + await webhookRequest.save(); + } + } +} diff --git a/apps/api/src/app/services/index.ts b/apps/api/src/app/services/index.ts new file mode 100644 index 000000000..bdf4232e9 --- /dev/null +++ b/apps/api/src/app/services/index.ts @@ -0,0 +1,49 @@ +export * as AnalyticsService from './AnalyticsService'; +export * as BalancerService from './BalancerService'; +export * as BrandService from './BrandService'; +export * as CanvasService from './CanvasService'; +export * as ClaimService from './ClaimService'; +export * as ContractService from './ContractService'; +export * as DiscordService from './DiscordService'; +export * as ERC1155Service from './ERC1155Service'; +export * as ERC20Service from './ERC20Service'; +export * as ERC721Service from './ERC721Service'; +export * as GalachainService from './GalachainService'; +export * as GitcoinService from './GitcoinService'; +export * as IPFSService from './IPFSService'; +export * as IdentityService from './IdentityService'; +export * as ImageService from './ImageService'; +export * as InvoiceService from './InvoiceService'; +export * as LiquidityService from './LiquidityService'; +export * as LockService from './LockService'; +export * as MailService from './MailService'; +export * as NotificationService from './NotificationService'; +export * as ParticipantService from './ParticipantService'; +export * as PaymentService from './PaymentService'; +export * as PointBalanceService from './PointBalanceService'; +export * as PoolService from './PoolService'; +export * as QuestCustomService from './QuestCustomService'; +export * as QuestDailyService from './QuestDailyService'; +export * as QuestDiscordService from './QuestDiscordService'; +export * as QuestGitcoinService from './QuestGitcoinService'; +export * as QuestInviteService from './QuestInviteService'; +export * as QuestService from './QuestService'; +export * as QuestSocialService from './QuestSocialService'; +export * as QuestWeb3Service from './QuestWeb3Service'; +export * as QuestWebhookService from './QuestWebhookService'; +export * as ReCaptchaService from './ReCaptchaService'; +export * as RewardCoinService from './RewardCoinService'; +export * as RewardCouponService from './RewardCouponService'; +export * as RewardCustomService from './RewardCustomService'; +export * as RewardDiscordRoleService from './RewardDiscordRoleService'; +export * as RewardGalachainService from './RewardGalachainService'; +export * as RewardNFTService from './RewardNFTService'; +export * as RewardService from './RewardService'; +export * as SafeService from './SafeService'; +export * as THXService from './THXService'; +export * as TransactionService from './TransactionService'; +export * as TwitterCacheService from './TwitterCacheService'; +export * as TwitterQueryService from './TwitterQueryService'; +export * as VoteEscrowService from './VoteEscrowService'; +export * as WalletService from './WalletService'; +export * as WebhookService from './WebhookService'; diff --git a/apps/api/src/app/services/interfaces/IGalaService.ts b/apps/api/src/app/services/interfaces/IGalaService.ts new file mode 100644 index 000000000..67731f3cd --- /dev/null +++ b/apps/api/src/app/services/interfaces/IGalaService.ts @@ -0,0 +1,26 @@ +import { TokenInstanceKey, TokenClassKey, RegisterUserDto, UserProfile } from '@gala-chain/api'; + +interface CustomProfileAPI { + GetProfile(privateKey: string): Promise<UserProfile>; + RegisterEthUser(publicKey: string): Promise<RegisterUserDto>; +} + +interface CustomTokenAPI { + CoinBalanceOf({ tokenInstance, owner }: { tokenInstance: TokenInstanceKey; owner: string }): Promise<any>; + CoinCreate( + tokenInfo: { + image: string; + name: string; + description: string; + symbol: string; + decimals: number; + maxSupply: any; + }, + privateKey: string, + ): Promise<TokenClassKey>; + CoinApprove(options: { spender: string; amount: number }, privateKey: string): Promise<any>; + CoinMint(options: { to: string; amount: number }, privateKey: string): Promise<TokenClassKey>; + CoinTransfer(options: { to: string; amount: number }, privateKey: string): Promise<any>; +} + +export { CustomProfileAPI, CustomTokenAPI }; diff --git a/apps/api/src/app/services/interfaces/IQuestService.ts b/apps/api/src/app/services/interfaces/IQuestService.ts new file mode 100644 index 000000000..777650f9c --- /dev/null +++ b/apps/api/src/app/services/interfaces/IQuestService.ts @@ -0,0 +1,38 @@ +import { Model } from 'mongoose'; +import QuestInviteService from '../QuestInviteService'; +import QuestDiscordService from '../QuestDiscordService'; +import QuestTwitterService from '../QuestSocialService'; // Split +import QuestYouTubeService from '../QuestSocialService'; // Split +import QuestDailyService from '../QuestDailyService'; +import QuestCustomService from '../QuestCustomService'; +import QuestGitcoinService from '../QuestGitcoinService'; +import QuestWeb3Service from '../QuestWeb3Service'; +import QuestWebhookService from '../QuestWebhookService'; +import { QuestVariant } from '@thxnetwork/common/enums'; + +export interface IQuestService { + models: { quest: Model<TQuest>; entry: Model<TQuestEntry> }; + decorate(options: { quest: TQuest; account?: TAccount; data: Partial<TQuestEntry> }): Promise<TQuest>; + isAvailable(options: { quest: TQuest; account?: TAccount; data: Partial<TQuestEntry> }): Promise<TValidationResult>; + getAmount(options: { quest: TQuest; account?: TAccount }): Promise<number>; + getValidationResult(options: { + quest: TQuest; + account: TAccount; + data: Partial<TQuestEntry>; + }): Promise<TValidationResult>; + findEntryMetadata(options: { quest: TQuest }); +} + +export const serviceMap: { + [variant: number]: IQuestService; +} = { + [QuestVariant.Daily]: new QuestDailyService(), + [QuestVariant.Invite]: new QuestInviteService(), + [QuestVariant.Discord]: new QuestDiscordService(), + [QuestVariant.Twitter]: new QuestTwitterService(), + [QuestVariant.YouTube]: new QuestYouTubeService(), + [QuestVariant.Custom]: new QuestCustomService(), + [QuestVariant.Web3]: new QuestWeb3Service(), + [QuestVariant.Gitcoin]: new QuestGitcoinService(), + [QuestVariant.Webhook]: new QuestWebhookService(), +}; diff --git a/apps/api/src/app/services/interfaces/IRewardService.ts b/apps/api/src/app/services/interfaces/IRewardService.ts new file mode 100644 index 000000000..0ffd58ac3 --- /dev/null +++ b/apps/api/src/app/services/interfaces/IRewardService.ts @@ -0,0 +1,32 @@ +import { Model } from 'mongoose'; +import { WalletDocument } from '@thxnetwork/api/models'; + +export interface IRewardService { + models: { + reward: Model<TReward>; + payment: Model<TRewardPayment>; + }; + decorate(data: { reward: TReward; account?: TAccount }): Promise<TReward>; + decoratePayment(payment: TRewardPayment): Promise<TRewardPayment>; + getValidationResult(data: { + reward: TReward; + wallet?: WalletDocument; + safe?: WalletDocument; + account?: TAccount; + }): Promise<TValidationResult>; + create(data: Partial<TReward>): Promise<TReward>; + update(reward: TReward, updates: Partial<TReward>): Promise<TReward>; + remove(reward: TReward): Promise<void>; + findById(id: string): Promise<TReward>; + createPayment({ + reward, + account, + safe, + wallet, + }: { + reward: TReward; + account: TAccount; + safe: WalletDocument; + wallet?: WalletDocument; + }): Promise<TValidationResult | void>; +} diff --git a/apps/api/src/app/services/maps/quests.ts b/apps/api/src/app/services/maps/quests.ts new file mode 100644 index 000000000..bd13ebc3d --- /dev/null +++ b/apps/api/src/app/services/maps/quests.ts @@ -0,0 +1,114 @@ +import { AccessTokenKind, QuestSocialRequirement, OAuthScope, OAuthRequiredScopes } from '@thxnetwork/common/enums'; +import { logger } from '@thxnetwork/api/util/logger'; +import DiscordDataProxy from '@thxnetwork/api/proxies/DiscordDataProxy'; +import TwitterDataProxy from '@thxnetwork/api/proxies/TwitterDataProxy'; +import YouTubeDataProxy from '@thxnetwork/api/proxies/YoutubeDataProxy'; + +export const requirementMap: { + [interaction: number]: (account: TAccount, quest: TQuestSocial) => Promise<TValidationResult>; +} = { + [QuestSocialRequirement.YouTubeLike]: async (account, quest) => { + return await YouTubeDataProxy.validateLike(account, quest.content); + }, + [QuestSocialRequirement.YouTubeSubscribe]: async (account, quest) => { + return await YouTubeDataProxy.validateSubscribe(account, quest.content); + }, + [QuestSocialRequirement.TwitterLike]: async (account, quest) => { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Like verification started`); + + const validationResultUser = await TwitterDataProxy.validateUser(account, quest); + if (!validationResultUser.result) return validationResultUser; + const validationResultLike = await TwitterDataProxy.validateLike(account, quest); + if (!validationResultLike.result) return validationResultLike; + }, + [QuestSocialRequirement.TwitterRetweet]: async (account, quest) => { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Repost verification started`); + + const validationResultUser = await TwitterDataProxy.validateUser(account, quest); + if (!validationResultUser.result) return validationResultUser; + const validationResultRepost = await TwitterDataProxy.validateRetweet(account, quest); + if (!validationResultRepost.result) return validationResultRepost; + }, + [QuestSocialRequirement.TwitterLikeRetweet]: async (account, quest) => { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} LikeRepost verification started`); + + const validationResultUser = await TwitterDataProxy.validateUser(account, quest); + if (!validationResultUser.result) return validationResultUser; + const validationResultLike = await TwitterDataProxy.validateLike(account, quest); + if (!validationResultLike.result) return validationResultLike; + const validationResultRepost = await TwitterDataProxy.validateRetweet(account, quest); + if (!validationResultRepost.result) return validationResultRepost; + }, + [QuestSocialRequirement.TwitterFollow]: async (account, quest) => { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Follow verification started`); + + const resultUser = await TwitterDataProxy.validateUser(account, quest); + if (!resultUser.result) return resultUser; + const validationResultFollow = await TwitterDataProxy.validateFollow(account, quest.content); + if (!validationResultFollow.result) return validationResultFollow; + }, + [QuestSocialRequirement.TwitterQuery]: async (account, quest) => { + logger.info(`[${quest.poolId}][${account.sub}] X Quest ${quest._id} Message verification started`); + const resultUser = await TwitterDataProxy.validateUser(account, quest); + if (!resultUser.result) return resultUser; + const validationResultMessage = await TwitterDataProxy.validateQuery(account, quest); + if (!validationResultMessage.result) return validationResultMessage; + }, + [QuestSocialRequirement.DiscordGuildJoined]: async (account, quest) => { + return await DiscordDataProxy.validateGuildJoined(account, quest.content); + }, + [QuestSocialRequirement.DiscordGuildRole]: async (account, quest) => { + const { roleId } = JSON.parse(quest.contentMetadata); + return await DiscordDataProxy.validateGuildRole(account, quest.content, roleId); + }, + [QuestSocialRequirement.DiscordMessage]: async (account, quest) => { + return { result: true, reason: '' }; + }, + [QuestSocialRequirement.DiscordMessageReaction]: async (account, quest) => { + return { result: true, reason: '' }; + }, +}; + +export const tokenInteractionMap: { [interaction: number]: { kind: AccessTokenKind; scopes: OAuthScope[] } } = { + [QuestSocialRequirement.YouTubeLike]: { + kind: AccessTokenKind.Google, + scopes: OAuthRequiredScopes.GoogleYoutubeLike, + }, + [QuestSocialRequirement.YouTubeSubscribe]: { + kind: AccessTokenKind.Google, + scopes: OAuthRequiredScopes.GoogleYoutubeSubscribe, + }, + [QuestSocialRequirement.TwitterLike]: { + kind: AccessTokenKind.Twitter, + scopes: OAuthRequiredScopes.TwitterValidateLike, + }, + [QuestSocialRequirement.TwitterRetweet]: { + kind: AccessTokenKind.Twitter, + scopes: OAuthRequiredScopes.TwitterValidateRepost, + }, + [QuestSocialRequirement.TwitterFollow]: { + kind: AccessTokenKind.Twitter, + scopes: OAuthRequiredScopes.TwitterValidateFollow, + }, + [QuestSocialRequirement.TwitterQuery]: { kind: AccessTokenKind.Twitter, scopes: OAuthRequiredScopes.TwitterAuth }, + [QuestSocialRequirement.TwitterLikeRetweet]: { + kind: AccessTokenKind.Twitter, + scopes: OAuthRequiredScopes.TwitterValidateLike, + }, + [QuestSocialRequirement.DiscordGuildJoined]: { + kind: AccessTokenKind.Discord, + scopes: OAuthRequiredScopes.DiscordValidateGuild, + }, + [QuestSocialRequirement.DiscordGuildRole]: { + kind: AccessTokenKind.Discord, + scopes: OAuthRequiredScopes.DiscordAuth, + }, + [QuestSocialRequirement.DiscordMessage]: { + kind: AccessTokenKind.Discord, + scopes: OAuthRequiredScopes.DiscordAuth, + }, + [QuestSocialRequirement.DiscordMessageReaction]: { + kind: AccessTokenKind.Discord, + scopes: OAuthRequiredScopes.DiscordAuth, + }, +}; diff --git a/apps/api/src/app/types/augment-express-request.d.ts b/apps/api/src/app/types/augment-express-request.d.ts new file mode 100644 index 000000000..de7385dff --- /dev/null +++ b/apps/api/src/app/types/augment-express-request.d.ts @@ -0,0 +1,11 @@ +namespace Express { + interface Request { + origin?: string; + auth?: any; + rawBody?: string; + account?: TAccount; + wallet?: WalletDocument; + campaign?: PoolDocument; + quest?: TQuest; + } +} diff --git a/apps/api/src/app/types/cors.d.ts b/apps/api/src/app/types/cors.d.ts new file mode 100644 index 000000000..8524732d2 --- /dev/null +++ b/apps/api/src/app/types/cors.d.ts @@ -0,0 +1 @@ +declare module 'cors'; diff --git a/apps/api/src/app/types/jsonwebtoken.d.ts b/apps/api/src/app/types/jsonwebtoken.d.ts new file mode 100644 index 000000000..0493c0083 --- /dev/null +++ b/apps/api/src/app/types/jsonwebtoken.d.ts @@ -0,0 +1 @@ +declare module 'jsonwebtoken'; diff --git a/apps/api/src/app/types/migrate-mongo.d.ts b/apps/api/src/app/types/migrate-mongo.d.ts new file mode 100644 index 000000000..4e6e50f6d --- /dev/null +++ b/apps/api/src/app/types/migrate-mongo.d.ts @@ -0,0 +1,110 @@ +// Copied type definitions for migrate-mongo here since the @types/migrate-mongo +// lags behind the current version. Once @types/migrate-mongo@^8.2.3 is available +// we can install that again and remove this file. +// +// Project: https://github.com/seppevs/migrate-mongo#readme +// Definitions by: Amit Beckenstein <https://github.com/amitbeck> +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Minimum TypeScript Version: 3.2 + +import * as mongo from 'mongodb'; +declare module 'migrate-mongo' { + export function init(): Promise<void>; + export function create(description: string): Promise<string>; + export namespace database { + function connect(): Promise<{ + client: mongo.MongoClient; + db: mongo.Db & { close: mongo.MongoClient['close'] }; + }>; + } + export namespace config { + /** + * @internal + */ + const DEFAULT_CONFIG_FILE_NAME: string; + /** + * @internal + */ + function shouldExist(): Promise<void>; + /** + * @internal + */ + function shouldNotExist(): Promise<void>; + /** + * @internal + */ + function getConfigFilename(): string; + /** + * Read the `migrate-mongo-config.js` file. + */ + function read(): Promise<Config>; + /** + * Set the passed config object. + * @param config The config object. + */ + function set(config: Partial<Config>): void; + + interface Config { + mongodb: { + url: Parameters<typeof mongo.MongoClient['connect']>[0]; + databaseName?: mongo.Db['databaseName']; + options?: mongo.MongoClientOptions; + }; + /** + * The migrations dir, can be an relative or absolute path. + */ + migrationsDir?: string | undefined; + /** + * The MongoDB collection where the applied changes are stored. + */ + changelogCollectionName: string; + } + } + + /** + * Apply all pending migrations. + * @example + * ```js + * const db = await database.connect(); + * const migrated = await up(db); + * migrated.forEach(fileName => console.log('Migrated:', fileName)); + * ``` + * If an an error occurred, the promise will reject and won't continue with the rest of the pending migrations. + */ + export function up(db: mongo.Db, client: mongo.MongoClient): Promise<string[]>; + /** + * Revert (only) the last applied migration. + * @example + * ```js + * const db = await database.connect(); + * const migratedDown = await down(db); + * migratedDown.forEach(fileName => console.log('Migrated Down:', fileName)); + * ``` + */ + export function down(db: mongo.Db, client: mongo.MongoClient): Promise<string[]>; + export function status(db: mongo.Db): Promise<MigrationStatus[]>; + + export interface MigrationStatus { + fileName: string; + /** + * Either "PENDING" or a JSON date. + */ + appliedAt: string; + } + + /** + * Type of `up()` and `down()` functions for migration scripts. + */ + export type MigrationFunction = + | ((db: mongo.Db, client: mongo.MongoClient) => Promise<void>) + /** + * @deprecated Callbacks are supported for backwards compatibility. + * New migration scripts should be written using `Promise`s and/or `async` & `await`. It's easier to read and write. + */ + | ((db: mongo.Db, next: mongo.MongoCallback<mongo.UpdateWriteOpResult>) => void) + /** + * @deprecated Callbacks are supported for backwards compatibility. + * New migration scripts should be written using `Promise`s and/or `async` & `await`. It's easier to read and write. + */ + | ((db: mongo.Db, client: mongo.MongoClient, next: mongo.MongoCallback<mongo.UpdateWriteOpResult>) => void); +} diff --git a/apps/api/src/app/types/morgan-json.d.ts b/apps/api/src/app/types/morgan-json.d.ts new file mode 100644 index 000000000..2b861c8c3 --- /dev/null +++ b/apps/api/src/app/types/morgan-json.d.ts @@ -0,0 +1 @@ +declare module 'morgan-json'; diff --git a/apps/api/src/app/types/morgan.d.ts b/apps/api/src/app/types/morgan.d.ts new file mode 100644 index 000000000..0b6637ede --- /dev/null +++ b/apps/api/src/app/types/morgan.d.ts @@ -0,0 +1 @@ +declare module 'morgan'; diff --git a/apps/api/src/app/types/swagger-autogen.d.ts b/apps/api/src/app/types/swagger-autogen.d.ts new file mode 100644 index 000000000..061dbe38b --- /dev/null +++ b/apps/api/src/app/types/swagger-autogen.d.ts @@ -0,0 +1 @@ +declare module 'swagger-autogen'; diff --git a/apps/api/src/app/types/swagger-ui-express.d.ts b/apps/api/src/app/types/swagger-ui-express.d.ts new file mode 100644 index 000000000..d7910fe9e --- /dev/null +++ b/apps/api/src/app/types/swagger-ui-express.d.ts @@ -0,0 +1 @@ +declare module 'swagger-ui-express'; diff --git a/apps/api/src/app/util/agenda.ts b/apps/api/src/app/util/agenda.ts new file mode 100644 index 000000000..c1ba701f2 --- /dev/null +++ b/apps/api/src/app/util/agenda.ts @@ -0,0 +1,64 @@ +import db from './database'; +import { Agenda, Job } from '@hokify/agenda'; +import { updatePendingTransactions } from '@thxnetwork/api/jobs/updatePendingTransactions'; +import { sendPoolAnalyticsReport } from '@thxnetwork/api/jobs/sendPoolAnalyticsReport'; +import { updateCampaignRanks } from '@thxnetwork/api/jobs/updateCampaignRanks'; +import { updateParticipantRanks } from '@thxnetwork/api/jobs/updateParticipantRanks'; +import { logger } from './logger'; +import { MONGODB_URI } from '../config/secrets'; +import { JobType } from '@thxnetwork/common/enums'; +import SafeService from '@thxnetwork/api/services/SafeService'; +import WebhookService from '../services/WebhookService'; +import QuestService from '../services/QuestService'; +import TwitterCacheService from '../services/TwitterCacheService'; +import InvoiceService from '../services/InvoiceService'; +import BalancerService from '../services/BalancerService'; +import RewardService from '../services/RewardService'; +import PaymentService from '../services/PaymentService'; +import TwitterQueryService from '../services/TwitterQueryService'; +import VoteEscrowService from '../services/VoteEscrowService'; + +const agenda = new Agenda({ + db: { + address: MONGODB_URI, + collection: 'jobs', + }, + maxConcurrency: 1, + lockLimit: 1, + processEvery: '1 second', +}); + +agenda.define(JobType.UpdateCampaignRanks, updateCampaignRanks); +agenda.define(JobType.UpdateParticipantRanks, updateParticipantRanks); +agenda.define(JobType.UpdatePendingTransactions, updatePendingTransactions); +agenda.define(JobType.CreateTwitterQuests, () => TwitterQueryService.searchJob()); +agenda.define(JobType.CreateQuestEntry, (job: Job) => QuestService.createEntryJob(job)); +agenda.define(JobType.CreateRewardPayment, (job: Job) => RewardService.createPaymentJob(job)); +agenda.define(JobType.DeploySafe, (job: Job) => SafeService.createJob(job)); +agenda.define(JobType.SendCampaignReport, sendPoolAnalyticsReport); +agenda.define(JobType.RequestAttemp, (job: Job) => WebhookService.requestAttemptJob(job)); +agenda.define(JobType.UpdateTwitterLikeCache, (job: Job) => TwitterCacheService.updateLikeCacheJob(job)); +agenda.define(JobType.UpdateTwitterRepostCache, (job: Job) => TwitterCacheService.updateRepostCacheJob(job)); +agenda.define(JobType.UpsertInvoices, () => InvoiceService.upsertJob()); +agenda.define(JobType.UpdatePrices, () => BalancerService.updatePricesJob()); +agenda.define(JobType.UpdateAPR, () => BalancerService.updateMetricsJob()); +agenda.define(JobType.AssertPayments, () => PaymentService.assertPaymentsJob()); +agenda.define(JobType.ClaimExternalRewards, () => VoteEscrowService.claimExternalRewardsJob()); + +db.connection.once('open', async () => { + await agenda.start(); + + await agenda.every('10 seconds', JobType.UpdatePrices); + await agenda.every('10 seconds', JobType.UpdatePendingTransactions); + await agenda.every('5 minutes', JobType.UpdateCampaignRanks); + await agenda.every('15 minutes', JobType.UpsertInvoices); + await agenda.every('15 minutes', JobType.UpdateAPR); + await agenda.every('1 day', JobType.CreateTwitterQuests); + await agenda.every('1 day', JobType.AssertPayments); + await agenda.every('1 day', JobType.ClaimExternalRewards); + await agenda.every('0 9 * * MON', JobType.SendCampaignReport); + + logger.info('AgendaJS started job processor'); +}); + +export { agenda, JobType }; diff --git a/apps/api/src/app/util/alchemy.ts b/apps/api/src/app/util/alchemy.ts new file mode 100644 index 000000000..1f2c20387 --- /dev/null +++ b/apps/api/src/app/util/alchemy.ts @@ -0,0 +1,48 @@ +import { Network, Alchemy, OwnedNft } from 'alchemy-sdk'; +import { ALCHEMY_API_KEY } from '../config/secrets'; +import { logger } from './logger'; +import { IPFS_BASE_URL } from '@thxnetwork/api/config/secrets'; + +export const alchemy = new Alchemy({ + apiKey: ALCHEMY_API_KEY, + network: Network.MATIC_MAINNET, +}); + +export async function getNFTsForOwner(owner: string, contractAddress: string) { + const pageSize = 100; + let pageKey = 0, + pageCount = 1, + ownedNfts: OwnedNft[] = []; + + while (pageKey < pageCount) { + try { + const key = String(++pageKey); + const result = await alchemy.nft.getNftsForOwner(owner, { + contractAddresses: [contractAddress], + omitMetadata: false, + pageSize, + pageKey: key, + }); + const totalCount = Number(result.totalCount); + + // If total is less than size there will only be 1 page, if not round up total / size + // to get the max amount of pages + pageCount = totalCount < pageSize ? 1 : Math.ceil(totalCount / pageSize); + + ownedNfts = ownedNfts.concat(result.ownedNfts); + } catch (error) { + logger.error(error); + } + } + + return ownedNfts; +} + +export function parseIPFSImageUrl(url = 'ipfs://QmdnhWN8VjX45BfX7sJuUnwcr7HU9YcDPPLCPQhSuuyjZ3/0.png') { + const ipfsPrefix = 'ipfs://'; + if (url.startsWith(ipfsPrefix)) { + const ipfsPath = url.substring(ipfsPrefix.length); + return IPFS_BASE_URL + ipfsPath; + } + return url; +} diff --git a/apps/api/src/app/util/auth.ts b/apps/api/src/app/util/auth.ts new file mode 100644 index 000000000..af39da296 --- /dev/null +++ b/apps/api/src/app/util/auth.ts @@ -0,0 +1,52 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { THXError } from './errors'; +import { AUTH_CLIENT_ID, AUTH_CLIENT_SECRET, AUTH_URL } from '@thxnetwork/api/config/secrets'; +import { AxiosResponse } from 'axios'; + +class AuthAccesTokenRequestError extends THXError { + message = 'Auth access token request failed'; +} + +let authAccessToken = ''; +let authAccessTokenExpires = 0; + +export const authClient = async (options: AxiosRequestConfig) => { + try { + axios.defaults.baseURL = AUTH_URL; + return await axios(options); + } catch (error) { + if (error && error.response && error.response.status >= 400 && error.response.status <= 600) { + return error.response as AxiosResponse; + } + } +}; + +async function requestAuthAccessToken() { + const data = new URLSearchParams(); + data.append('grant_type', 'client_credentials'); + data.append('scope', 'openid accounts:read accounts:write'); + + const r = await authClient({ + url: '/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + Buffer.from(`${AUTH_CLIENT_ID}:${AUTH_CLIENT_SECRET}`).toString('base64'), + }, + data, + }); + + if (r.status !== 200) throw new AuthAccesTokenRequestError(); + + return r.data; +} + +export async function getAuthAccessToken() { + if (!authAccessTokenExpires || Date.now() > authAccessTokenExpires) { + const { access_token, expires_in } = await requestAuthAccessToken(); + authAccessToken = access_token; + authAccessTokenExpires = Date.now() + expires_in * 1000; + } + + return `Bearer ${authAccessToken}`; +} diff --git a/apps/api/src/app/util/code.ts b/apps/api/src/app/util/code.ts new file mode 100644 index 000000000..ec31c91bd --- /dev/null +++ b/apps/api/src/app/util/code.ts @@ -0,0 +1,12 @@ +import { ChainId } from '@thxnetwork/common/enums'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { NoDataAtAddressError } from '@thxnetwork/api/util/errors'; + +export async function getCodeForAddressOnNetwork(address: string, chainId: ChainId) { + const { web3 } = getProvider(chainId); + const code = await web3.eth.getCode(address); + + if (code === '0x') { + throw new NoDataAtAddressError(address); + } +} diff --git a/apps/campaign/src/assets/.gitkeep b/apps/api/src/app/util/condition.ts similarity index 100% rename from apps/campaign/src/assets/.gitkeep rename to apps/api/src/app/util/condition.ts diff --git a/apps/api/src/app/util/database.ts b/apps/api/src/app/util/database.ts new file mode 100644 index 000000000..d4192af88 --- /dev/null +++ b/apps/api/src/app/util/database.ts @@ -0,0 +1,59 @@ +import mongoose from 'mongoose'; +import { logger } from './logger'; +import { v4 } from 'uuid'; + +const connect = async (url: string) => { + mongoose.connection.on('error', (err) => { + logger.error(`MongoDB connection error. Please make sure MongoDB is running. ${err}`); + }); + + mongoose.connection.on('reconnectFailed', () => { + logger.error('Unable to reconnect to MongoDB'); + process.exit(); + }); + + mongoose.connection.on('open', () => { + logger.info(`MongoDB successfully connected to ${url.split('@')[1]}`); + }); + + mongoose.connection.on('close', () => { + logger.info(`MongoDB successfully closed connection`); + }); + + if (mongoose.connection.readyState === 0) { + await mongoose.connect(url); + } +}; + +const truncate = async () => { + if (mongoose.connection.readyState !== 0) { + const { collections } = mongoose.connection; + const promises = Object.keys(collections).map((collection) => { + return mongoose.connection.collection(collection).deleteMany({}); + }); + await Promise.all(promises); + } +}; + +const readyState = () => { + return mongoose.connection.readyState; +}; + +const disconnect = async () => { + if (mongoose.connection.readyState !== 0) { + await mongoose.disconnect(); + } +}; + +const createUUID = () => { + return v4(); +}; + +export default { + connect, + truncate, + disconnect, + readyState, + connection: mongoose.connection, + createUUID, +}; diff --git a/apps/api/src/app/util/date.ts b/apps/api/src/app/util/date.ts new file mode 100644 index 000000000..c4a1c04f1 --- /dev/null +++ b/apps/api/src/app/util/date.ts @@ -0,0 +1,18 @@ +export function addMinutes(date: Date, minutes: number) { + return new Date(date.getTime() + minutes * 60000); +} + +export function subMinutes(date: Date, minutes: number) { + return new Date(date.getTime() - minutes * 60000); +} + +export function formatDate(date: Date) { + const yyyy = date.getFullYear(); + let mm: any = date.getMonth() + 1; // Months start at 0! + let dd: any = date.getDate(); + + if (dd < 10) dd = '0' + dd; + if (mm < 10) mm = '0' + mm; + + return yyyy + '-' + mm + '-' + dd; +} diff --git a/apps/api/src/app/util/dictionaries.ts b/apps/api/src/app/util/dictionaries.ts new file mode 100644 index 000000000..456c775fc --- /dev/null +++ b/apps/api/src/app/util/dictionaries.ts @@ -0,0 +1,52 @@ +export const celebratoryWords = [ + 'Huray!', + 'Yay!', + 'Whoop!', + 'Hoorah!', + 'Woo-hoo!', + 'Yippee!', + 'Bravo!', + 'Cheers!', + 'Yee-haw!', + 'Hip-hip-hooray!', + 'Awesome!', + 'Fantastic!', + 'Celebrate!', + 'Triumph!', + 'Victory!', + 'Delight!', + 'Excelsior!', + 'Amazing!', + 'Outstanding!', + 'Ecstatic!', + 'Thrilling!', + 'Glorious!', + 'Exhilarating!', + 'Jubilation!', + 'Eureka!', + 'Huzzah!', + 'Applause!', + 'Delirious!', + 'Phenomenal!', + 'Splendid!', + 'Magnificent!', + 'Terrific!', + 'Superb!', + 'Incredible!', + 'Astounding!', + 'Majestic!', + 'Wonderful!', + 'Marvelous!', + 'Electrifying!', + 'Fabulous!', + 'Glittering!', + 'Splendiferous!', + 'Stupendous!', + 'Ecstasy!', + 'Triumphant!', + 'Brilliant!', + 'Radiant!', + 'Euphoria!', + 'Unbelievable!', + 'Spectacular!', +]; diff --git a/apps/api/src/app/util/discord.ts b/apps/api/src/app/util/discord.ts new file mode 100644 index 000000000..9295f557d --- /dev/null +++ b/apps/api/src/app/util/discord.ts @@ -0,0 +1,33 @@ +import { Client, SlashCommandBuilder } from 'discord.js'; +import { REST, Routes } from 'discord.js'; +import { DISCORD_CLIENT_ID, BOT_TOKEN } from '../config/secrets'; +import { logger } from './logger'; +import { onAutoComplete } from '../events/InteractionCreated'; +import { Events } from 'discord.js'; + +const rest = new REST({ version: '10' }).setToken(BOT_TOKEN); + +export const commandRegister = async (commandList: SlashCommandBuilder[]) => { + try { + const commands = commandList.map((cmd) => cmd.toJSON()); + await rest.put(Routes.applicationCommands(DISCORD_CLIENT_ID), { body: commands }); + logger.info(`Successfully reloaded ${commands.length} application (/) commands.`); + } catch (error) { + logger.error(`Some error happened while loading application (/) commands.`); + logger.error(error); + } +}; + +export const eventRegister = (client: Client<boolean>, router: { [key: string]: any }) => { + Object.keys(router).forEach((key) => { + client.on(key, router[key]); + }); + client.on(Events.InteractionCreate, onAutoComplete); + client.on('error', (error) => { + console.error('Discord.js error:', error); + }); +}; + +export function discordColorToHex(discordColorCode) { + return `#${discordColorCode.toString(16).padStart(6, '0')}`; +} diff --git a/apps/api/src/app/util/errors.ts b/apps/api/src/app/util/errors.ts new file mode 100644 index 000000000..e52f9e978 --- /dev/null +++ b/apps/api/src/app/util/errors.ts @@ -0,0 +1,187 @@ +class THXError extends Error { + message: string; + + constructor(message?: string) { + super(message); + this.name = this.constructor.name; + this.message = message || 'No error message'; + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + } +} + +class NoUserFound extends THXError { + constructor() { + super('Could not find a user for this address'); + } +} + +class NotAMemberError extends THXError { + constructor(address: string, assetPool: string) { + super(`${address} is not a member of assetPool ${assetPool}`); + } +} +class AlreadyAMemberError extends THXError { + constructor(address: string, assetPool: string) { + super(`${address} is already a member of assetPool ${assetPool}`); + } +} + +class NoDataAtAddressError extends THXError { + constructor(address: string) { + super(`No data found at address ${address}`); + } +} + +class THXHttpError extends THXError { + status: number; + constructor(message?: string, status?: number) { + super(message); + if (status) { + this.status = status; + } + } +} + +class BadRequestError extends THXHttpError { + status = 400; + constructor(message?: string) { + super(message || 'Bad Request'); + } +} + +class UnauthorizedError extends THXHttpError { + status = 401; + constructor(message?: string) { + super(message || 'Unauthorized'); + } +} + +class ForbiddenError extends THXHttpError { + status = 403; + constructor(message?: string) { + super(message || 'Forbidden'); + } +} + +class NotFoundError extends THXHttpError { + status = 404; + constructor(message?: string) { + super(message || 'Not Found'); + } +} + +class ConflictError extends THXHttpError { + status = 409; + constructor(message?: string) { + super(message || 'Conflict'); + } +} + +class InternalServerError extends THXHttpError { + status = 500; + constructor(message?: string) { + super(message || 'Internal Server Error'); + } +} + +class NotImplementedError extends THXHttpError { + status = 501; + constructor(message?: string) { + super(message || 'Not Implemented'); + } +} + +class BadGatewayError extends THXHttpError { + status = 502; + constructor(message?: string) { + super(message || 'Bad Gateway'); + } +} + +class PromoCodeNotFoundError extends NotFoundError { + message = 'Could not find this promo code'; +} + +class SubjectUnauthorizedError extends ForbiddenError { + message = 'Not authorized for subject of access token'; +} + +class AudienceUnauthorizedError extends UnauthorizedError { + message = 'Not authorized for audience of access token'; +} + +class AmountExceedsAllowanceError extends BadRequestError { + message = 'Transfer amount exceeds allowance'; +} + +class InsufficientBalanceError extends BadRequestError { + message = 'Transfer amount exceeds balance'; +} + +class InsufficientAllowanceError extends BadRequestError { + message = 'Requested amount exceeds allowance'; +} + +class TokenPaymentFailedError extends InternalServerError { + message = 'Transfer did not succeed'; +} +class GetPastTransferEventsError extends InternalServerError { + message = 'GetPastEvents for Transfer event failed in callback.'; +} +class GetPastWithdrawnEventsError extends InternalServerError { + message = 'GetPastEvents for Withdrawn event failed in callback.'; +} + +class DuplicateEmailError extends BadRequestError { + message = 'An account with this e-mail address already exists.'; +} + +class GetPastWithdrawPollCreatedEventsError extends InternalServerError { + message = 'GetPastEvents for WithdrawPollCreated event failed in callback.'; +} +class MaxFeePerGasExceededError extends THXError { + message = 'MaxFeePerGas from oracle exceeds configured cap'; +} +class NoFeeDataError extends THXError { + message = 'Could not get fee data from oracle'; +} + +class DiscordDisconnected extends THXError { + message = 'Please sign in to your THX account and connect your Discord account.'; +} + +class DiscordSafeNotFound extends THXError { + message = 'Please sign in to your THX account so we can deploy your Safe multisig wallet.'; +} + +export { + DiscordSafeNotFound, + DiscordDisconnected, + THXError, + NoUserFound, + THXHttpError, + BadRequestError, + UnauthorizedError, + ForbiddenError, + NotFoundError, + ConflictError, + NotImplementedError, + BadGatewayError, + InternalServerError, + PromoCodeNotFoundError, + SubjectUnauthorizedError, + AudienceUnauthorizedError, + AmountExceedsAllowanceError, + InsufficientBalanceError, + TokenPaymentFailedError, + GetPastTransferEventsError, + GetPastWithdrawnEventsError, + DuplicateEmailError, + GetPastWithdrawPollCreatedEventsError, + NotAMemberError, + AlreadyAMemberError, + NoDataAtAddressError, + MaxFeePerGasExceededError, + InsufficientAllowanceError, + NoFeeDataError, +}; diff --git a/apps/api/src/app/util/events.ts b/apps/api/src/app/util/events.ts new file mode 100644 index 000000000..8946815f2 --- /dev/null +++ b/apps/api/src/app/util/events.ts @@ -0,0 +1,74 @@ +import { ethers } from 'ethers'; +import { THXError } from './errors'; +import { logger } from './logger'; + +export class ExpectedEventNotFound extends THXError { + constructor(event: string) { + super(`Event ${event} expected in eventlog but not found.`); + } +} + +export function parseArgs(args: any) { + const returnValues: any = {}; + for (const key of Object.keys(args)) { + if (isNaN(Number(key))) { + returnValues[key] = args[key]; + } + } + return returnValues; +} + +export function parseLog(abi: any, log: any) { + const contractInterface = new ethers.utils.Interface(abi); + try { + return contractInterface.parseLog(log); + } catch (e) { + logger.error(e.toString()); + return; + } +} + +export function hex2a(hex: any) { + let str = ''; + for (let i = 0; i < hex.length; i += 2) { + const v = parseInt(hex.substr(i, 2), 16); + if (v == 8) continue; // http://www.fileformat.info/info/unicode/char/0008/index.htm + if (v == 15) continue; + if (v == 16) continue; // http://www.fileformat.info/info/unicode/char/0010/index.htm + if (v == 14) continue; // https://www.fileformat.info/info/unicode/char/000e/index.htm + if (v) str += String.fromCharCode(v); + } + return str.trim(); +} + +export function findEvent(eventName: string, events: CustomEventLog[]): CustomEventLog { + return events.find((ev: any) => ev && ev.name === eventName); +} + +export function assertEvent(eventName: string, events: CustomEventLog[]): CustomEventLog { + const event = findEvent(eventName, events); + + if (!event) { + throw new ExpectedEventNotFound(eventName); + } + + return event; +} + +export interface CustomEventLog { + name: string; + args: any; + blockNumber: number; + transactionHash: string; +} + +export function parseLogs(abi: any, logs: any = []): CustomEventLog[] { + const contractInterface = new ethers.utils.Interface(abi); + return logs.map((log: any) => { + try { + return { ...log, ...contractInterface.parseLog(log) }; + } catch (e) { + return; + } + }); +} diff --git a/apps/api/src/app/util/galachain.ts b/apps/api/src/app/util/galachain.ts new file mode 100644 index 000000000..1f6593422 --- /dev/null +++ b/apps/api/src/app/util/galachain.ts @@ -0,0 +1,48 @@ +import path from 'path'; +import { CWD } from '../config/secrets'; +import { gcclient, HFClientConfig } from '@gala-chain/client'; + +enum GalachainRole { + Partner = 0, + Curator = 1, + User = 2, +} + +enum GalachainContract { + PublicKeyContract = 'PublicKeyContract', + GalaChainToken = 'GalaChainToken', +} + +const credentials: { [role: number]: HFClientConfig } = { + [GalachainRole.Partner]: { + orgMsp: 'PartnerOrg', + userId: 'admin', + userSecret: 'adminpw', + connectionProfilePath: path.resolve(CWD, 'app/connection-profiles/cpp-partner.json'), + }, + [GalachainRole.Curator]: { + orgMsp: 'CuratorOrg', + userId: 'admin', + userSecret: 'adminpw', + connectionProfilePath: path.resolve(CWD, 'app/connection-profiles/cpp-curator.json'), + }, + [GalachainRole.User]: { + orgMsp: 'UserOrg', + userId: 'admin', + userSecret: 'adminpw', + connectionProfilePath: path.resolve(CWD, 'app/connection-profiles/cpp-user.json'), + }, +}; + +const getClient = (role: GalachainRole) => { + const params = credentials[role]; + return gcclient.forConnectionProfile(params); +}; + +const getContract = (variant: GalachainContract) => ({ + channelName: 'product-channel', + chaincodeName: 'basic-product', + contractName: variant, +}); + +export { getContract, getClient, GalachainRole, GalachainContract }; diff --git a/apps/api/src/app/util/healthcheck.ts b/apps/api/src/app/util/healthcheck.ts new file mode 100644 index 000000000..12bdd0bcd --- /dev/null +++ b/apps/api/src/app/util/healthcheck.ts @@ -0,0 +1,34 @@ +import newrelic from 'newrelic'; +import { config, status } from 'migrate-mongo'; +import { connection } from 'mongoose'; +import { HealthCheck } from '@godaddy/terminus'; + +import migrateMongoConfig from '../config/migrate-mongo'; + +const dbConnected = async () => { + // https://mongoosejs.com/docs/api.html#connection_Connection-readyState + const { readyState } = connection; + // ERR_CONNECTING_TO_MONGO + if (readyState === 0 || readyState === 3) { + throw new Error('Mongoose has disconnected'); + } + // CONNECTING_TO_MONGO + if (readyState === 2) { + throw new Error('Mongoose is connecting'); + } +}; + +const migrationsApplied = async () => { + config.set(migrateMongoConfig); + const pendingMigrations = (await status(connection.db as any)).filter( + (migration) => migration.appliedAt === 'PENDING', + ); + if (pendingMigrations.length > 0) { + throw new Error('Not all migrations applied'); + } +}; + +export const healthCheck: HealthCheck = async () => { + newrelic.getTransaction().ignore(); + return Promise.all([dbConnected(), migrationsApplied()]); +}; diff --git a/apps/api/src/app/util/helpers.ts b/apps/api/src/app/util/helpers.ts new file mode 100644 index 000000000..788236765 --- /dev/null +++ b/apps/api/src/app/util/helpers.ts @@ -0,0 +1,23 @@ +export const pick = <T, K extends keyof T>(object: T, keys: K[]): Pick<T, K> => { + return Object.assign( + {}, + ...keys.map((key) => { + if (object && Object.prototype.hasOwnProperty.call(object, key)) { + return { [key]: object[key] }; + } + }), + ); +}; + +export const uniq = <T>(array: T[]): T[] => { + return [...new Set(array)]; +}; + +export const sleep = async (seconds: number) => { + await new Promise((resolve) => setTimeout(resolve, seconds * 1000)); +}; + +export const convertObjectIdToNumber = (objectId: string) => { + // Extract the timestamp part of the ObjectId (first 4 bytes) + return parseInt(objectId.toString().substring(0, 8), 16); +}; diff --git a/apps/api/src/app/util/index.ts b/apps/api/src/app/util/index.ts new file mode 100644 index 000000000..c5f595cf9 --- /dev/null +++ b/apps/api/src/app/util/index.ts @@ -0,0 +1 @@ +export * from './helpers'; diff --git a/apps/api/src/app/util/ip.ts b/apps/api/src/app/util/ip.ts new file mode 100644 index 000000000..ae0b16e77 --- /dev/null +++ b/apps/api/src/app/util/ip.ts @@ -0,0 +1,7 @@ +import { Request } from 'express'; + +function getIP(req: Request) { + return req.ip || req.header('x-forwarded-for'); +} + +export { getIP }; diff --git a/apps/api/src/app/util/jest/config.ts b/apps/api/src/app/util/jest/config.ts new file mode 100644 index 000000000..996f4e360 --- /dev/null +++ b/apps/api/src/app/util/jest/config.ts @@ -0,0 +1,91 @@ +import db from '@thxnetwork/api/util/database'; +import { mockStart } from './mock'; +import { safeVersion } from '@thxnetwork/api/services/ContractService'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { ChainId, WalletVariant } from '@thxnetwork/common/enums'; +import { + sub, + sub2, + sub3, + sub4, + userWalletAddress, + userWalletAddress2, + userWalletAddress3, + userWalletAddress4, +} from './constants'; +import { Wallet } from '@thxnetwork/api/models'; +import Safe, { SafeFactory } from '@safe-global/protocol-kit'; +import { contractNetworks } from '@thxnetwork/api/hardhat'; +import { poll } from '../polling'; +import { agenda } from '../agenda'; + +export async function beforeAllCallback(options = { skipWalletCreation: false }) { + mockStart(); + + const { web3, defaultAccount, ethAdapter } = getProvider(ChainId.Hardhat); + // Wait for this hardhat log: + const lastDeployedContractAddress = '0x58C0e64cBB7E5C7D0201A3a5c2D899cC70B0dc4c'; + const fn = () => web3.eth.getCode(lastDeployedContractAddress); + const fnCondition = (result: string) => result === '0x'; + + await poll(fn, fnCondition, 500); + + if (!options.skipWalletCreation) { + const chainId = ChainId.Hardhat; + const safeFactory = await SafeFactory.create({ + safeVersion, + ethAdapter, + contractNetworks, + }); + for (const entry of [ + { sub, userWalletAddress }, + { sub: sub2, userWalletAddress: userWalletAddress2 }, + { sub: sub3, userWalletAddress: userWalletAddress3 }, + ]) { + const safeAccountConfig = { + owners: [defaultAccount, entry.userWalletAddress], + threshold: 2, + }; + const safeAddress = await safeFactory.predictSafeAddress(safeAccountConfig); + + await Wallet.create({ + sub: entry.sub, + safeVersion, + address: safeAddress, + chainId, + variant: WalletVariant.Safe, + }); + + try { + await Safe.create({ + ethAdapter, + safeAddress, + contractNetworks, + }); + } catch (error) { + await safeFactory.deploySafe({ safeAccountConfig, options: { gasLimit: '3000000' } }); + } + } + + // Create wallet for metamask account + await Wallet.create({ + chainId: ChainId.Hardhat, + sub: sub4, + address: userWalletAddress4, + variant: WalletVariant.WalletConnect, + }); + } +} + +export async function afterAllCallback() { + await new Promise<void>((resolve) => { + // Listen for 'complete' event + agenda.on('complete', () => { + resolve(); + }); + }); + await agenda.stop(); + await agenda.cancel({}); + await agenda.purge(); + await db.truncate(); +} diff --git a/apps/api/src/app/util/jest/constants.ts b/apps/api/src/app/util/jest/constants.ts new file mode 100644 index 000000000..656144f62 --- /dev/null +++ b/apps/api/src/app/util/jest/constants.ts @@ -0,0 +1,89 @@ +import { AccountPlanType, AccountVariant } from '@thxnetwork/common/enums'; +import { getToken } from './jwt'; +import { toWei } from 'web3-utils'; +import { CYPRESS_EMAIL } from '@thxnetwork/api/config/secrets'; + +export const tokenName = 'Volunteers United'; +export const tokenSymbol = 'VUT'; +export const tokenTotalSupply = toWei('100000000'); +export const rewardWithdrawAmount = '1000'; +export const rewardWithdrawDuration = 60; +export const rewardWithdrawUnlockDate = '2022-04-20'; +export const COLLECTOR_PK = '0x794a8efb7e73278907197b0f65e1c32724810f0399e1a12feb1e6af6fb77dbff'; +export const VOTER_PK = '0x97093724e1748ebfa6aa2d2ec4ec68df8678423ab9a12eb2d27ddc74e35e5db9'; +export const DEPOSITOR_PK = '0x5a05e38394194379795422d2e8c1d33e90033d90defec4880174c39198f707e3'; +export const userEmail = CYPRESS_EMAIL; +export const userEmail2 = CYPRESS_EMAIL; +export const userPassword = 'mellonmellonmellon'; +export const userPassword2 = 'mellonmellonmellon'; +export const voterEmail = CYPRESS_EMAIL; +export const newAddress = '0x253cA584af3E458392982EF246066A6750Fa0735'; +export const MaxUint256 = '115792089237316195423570985008687907853269984665640564039457584007913129639935'; +export const sub = '6074cbdd1459355fae4b6a14'; +export const sub2 = '6074cbdd1459355fae4b6a15'; +export const sub3 = '6074cbdd1459355fae4b6a16'; +export const sub4 = '6074cbdd1459355fae4b6a17'; +export const userWalletAddress = '0x960911a62FdDf7BA84D0d3aD016EF7D15966F7Dc'; +export const userWalletAddress2 = '0xaf9d56684466fcFcEA0a2B7fC137AB864d642946'; +export const userWalletAddress3 = '0x861EFc0989DF42d793e3147214FfFcA4D124cAE8'; +export const userWalletAddress4 = '0x6e781b0af6204c3dc23b4a7fe049202125a4f849'; +export const userWalletPrivateKey = '0x794a8efb7e73278907197b0f65e1c32724810f0399e1a12feb1e6af6fb77dbff'; +export const userWalletPrivateKey2 = '0x97093724e1748ebfa6aa2d2ec4ec68df8678423ab9a12eb2d27ddc74e35e5db9'; +export const userWalletPrivateKey3 = '0x5a05e38394194379795422d2e8c1d33e90033d90defec4880174c39198f707e3'; +export const userWalletPrivateKey4 = '0x3b7fdd74a6c50a03d6e37f2d2c54e6fc73d67ff7d32858e55d80b2a5c9946b79'; + +export const account = { + sub, + plan: AccountPlanType.Lite, + email: CYPRESS_EMAIL, + address: userWalletAddress, + tokens: [], +}; +export const account2 = { + sub: sub2, + plan: AccountPlanType.Lite, + email: CYPRESS_EMAIL, + address: userWalletAddress2, + tokens: [], +}; + +export const account3 = { + sub: sub3, + plan: AccountPlanType.Lite, + variant: AccountVariant.EmailPassword, + address: userWalletAddress3, + tokens: [], +}; + +export const account4 = { + sub: sub4, + plan: AccountPlanType.Lite, + variant: AccountVariant.Metamask, + address: userWalletAddress4, +}; + +export const rewardId = 1; +export const requestUris = ['http://localhost:8080']; +export const redirectUris = ['http://localhost:8080']; +export const postLogoutRedirectUris = ['http://localhost:8080']; +export const clientId = 'xxxxxxx'; +export const clientSecret = 'xxxxxxxxxxxxxx'; +export const registrationAccessToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; + +export const authScopes = 'wallets:read wallets:write'; +export const dashboardScopes = + 'openid pools:read pools:write erc20:write erc20:read erc721:write erc721:read erc1155:write erc1155:read rewards:read rewards:write deposits:read deposits:write promotions:read promotions:write widgets:write widgets:read transactions:read swaprule:read swaprule:write claims:read erc20_rewards:read erc20_rewards:write erc721_rewards:read erc721_rewards:write referral_rewards:read referral_rewards:write pool_subscription:read custom_rewards:write custom_rewards:read'; +export const dashboardAccessToken = getToken(dashboardScopes); +export const dashboardAccessToken2 = getToken(dashboardScopes, sub2); +export const walletScopes = + 'openid rewards:read erc20:read erc721:read erc1155:read withdrawals:read withdrawals:write deposits:read deposits:write account:read account:write memberships:read memberships:write promotions:read payments:write payments:read relay:write transactions:read transactions:write swap:read swap:write swaprule:read claims:read wallets:read wallets:write erc20_rewards:read erc721_rewards:read referral_rewards:read point_balances:read'; +export const widgetScopes = + 'openid offline_access account:read account:write erc20:read erc721:read erc1155:read point_balances:read referral_rewards:read point_rewards:read wallets:read wallets:write pool_subscription:read pool_subscription:write claims:read'; +export const walletAccessToken = getToken(walletScopes); +export const walletAccessToken2 = getToken(walletScopes, sub); +export const walletAccessToken3 = getToken(walletScopes, sub2); +export const widgetAccessToken = getToken(widgetScopes, sub); +export const widgetAccessToken2 = getToken(widgetScopes, sub2); +export const widgetAccessToken3 = getToken(widgetScopes, sub3); +export const widgetAccessToken4 = getToken(widgetScopes, sub4); +export const authAccessToken = getToken(authScopes); diff --git a/apps/api/src/app/util/jest/erc1155.ts b/apps/api/src/app/util/jest/erc1155.ts new file mode 100644 index 000000000..bcd689b4e --- /dev/null +++ b/apps/api/src/app/util/jest/erc1155.ts @@ -0,0 +1,58 @@ +import { API_URL, MINIMUM_GAS_LIMIT, VERSION } from '@thxnetwork/api/config/secrets'; +import { ChainId } from '@thxnetwork/common/enums'; +import { getProvider } from '../network'; +import { getArtifact } from '@thxnetwork/api/hardhat'; + +export async function deployERC1155(chainId = ChainId.Hardhat) { + const { web3, defaultAccount } = getProvider(chainId); + const contractName = 'THXERC1155'; + const { abi, bytecode } = getArtifact(contractName); + const contract = new web3.eth.Contract(abi); + const baseURL = `${API_URL}/${VERSION}/erc1155/metadata/{id}`; + const fn = contract.deploy({ + data: bytecode, + arguments: [baseURL, defaultAccount], + }); + const data = fn.encodeABI(); + const estimate = await fn.estimateGas({ from: defaultAccount }); + const gas = estimate < Number(MINIMUM_GAS_LIMIT) ? MINIMUM_GAS_LIMIT : estimate; + const receipt = await web3.eth.sendTransaction({ + from: defaultAccount, + to: null, + data, + gas, + }); + + contract.options.address = receipt.contractAddress; + + return contract; +} + +export const mockGetNftsForOwner = (contractAddress: string) => { + return { + ownedNfts: [ + { + name: 'Test Collection', + description: 'Test Collection description', + image: { + originalUrl: 'ipfs://QmRvCinGkzqDdmSZ3PzQRyHbQVqaFLTDyfyMMD54Bwcjsi/1.png', + }, + contract: { + address: contractAddress, + }, + tokenId: '1', + tokenUri: 'https://ipfs.io/ipfs/QmRvCinGkzqDdmSZ3PzQRyHbQVqaFLTDyfyMMD54Bwcjsi', + collection: { + externalUrl: 'https://example.com', + }, + rawMetadata: { + name: '#1', + description: 'image description piece #1', + image: 'https://gateway.pinata.cloud/ipfs/QmemtAVJMkfUj3bAXee1H7vccbX6nC6Vbkbu6gBjdn1Kdh/1.png', + }, + }, + ], + pageKey: 1, + totalCount: 1, + }; +}; diff --git a/apps/api/src/app/util/jest/erc721.ts b/apps/api/src/app/util/jest/erc721.ts new file mode 100644 index 000000000..75e130cfe --- /dev/null +++ b/apps/api/src/app/util/jest/erc721.ts @@ -0,0 +1,60 @@ +import { API_URL, MINIMUM_GAS_LIMIT, VERSION } from '@thxnetwork/api/config/secrets'; +import { ChainId } from '@thxnetwork/common/enums'; +import { getProvider } from '../network'; +import { getArtifact } from '@thxnetwork/api/hardhat'; + +export async function deployERC721(nftName: string, nftSymbol: string) { + const { web3, defaultAccount } = getProvider(ChainId.Hardhat); + const contractName = 'THXERC721'; + const { abi, bytecode } = getArtifact(contractName); + const contract = new web3.eth.Contract(abi); + const baseURL = `${API_URL}/${VERSION}/erc721/metadata/`; + const fn = contract.deploy({ + data: bytecode, + arguments: [nftName, nftSymbol, baseURL, defaultAccount], + }); + const data = fn.encodeABI(); + const estimate = await fn.estimateGas({ from: defaultAccount }); + const gas = estimate < Number(MINIMUM_GAS_LIMIT) ? MINIMUM_GAS_LIMIT : estimate; + const receipt = await web3.eth.sendTransaction({ + from: defaultAccount, + to: null, + data, + gas, + }); + + contract.options.address = receipt.contractAddress; + + return contract; +} + +export const mockGetNftsForOwner = (contractAddress: string, nftName: string, nftSymbol: string) => { + return { + ownedNfts: [ + { + contract: { + address: contractAddress, + name: nftName, + symbol: nftSymbol, + }, + tokenId: '1', + tokenUri: 'https://ipfs.io/ipfs/QmRvCinGkzqDdmSZ3PzQRyHbQVqaFLTDyfyMMD54Bwcjsi', + rawMetadata: { + name: '#1', + description: 'image description piece #1', + image: 'https://gateway.pinata.cloud/ipfs/QmemtAVJMkfUj3bAXee1H7vccbX6nC6Vbkbu6gBjdn1Kdh/1.png', + }, + name: 'Test Collection', + description: 'Test Collection description', + image: { + originalUrl: 'ipfs://QmRvCinGkzqDdmSZ3PzQRyHbQVqaFLTDyfyMMD54Bwcjsi/1.png', + }, + collection: { + externalUrl: 'https://example.com', + }, + }, + ], + pageKey: 1, + totalCount: 1, + }; +}; diff --git a/apps/api/src/app/util/jest/images.ts b/apps/api/src/app/util/jest/images.ts new file mode 100644 index 000000000..886b3090c --- /dev/null +++ b/apps/api/src/app/util/jest/images.ts @@ -0,0 +1,6 @@ +import path from 'path'; +import fs from 'fs'; + +export function createImage() { + return fs.readFileSync(path.resolve(__dirname, 'test.jpg')); +} diff --git a/apps/api/src/app/util/jest/jwt.ts b/apps/api/src/app/util/jest/jwt.ts new file mode 100644 index 000000000..05c88cfe5 --- /dev/null +++ b/apps/api/src/app/util/jest/jwt.ts @@ -0,0 +1,77 @@ +import jwt from 'jsonwebtoken'; +import { AUTH_URL } from '../../config/secrets'; +import { dashboardScopes, sub, sub2, walletScopes } from './constants'; + +const privateKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAwaZ3afW0/zYy3HfJwAAr83PDdZvADuSJ6jTZk1+jprdHdG6P +zH9XaB6xhzvwTIJFcWuREkNSC06MDLCuvmZ8fj93FcNaZ2ZJ0LFvY4SODMDqFekE +5vD2Y15aSI2Y69qwKlVLphvEEXJ/FRqIHQX9wwCtwVsnqcLt/f5aNWRHyk2jwhz7 +IBm+dLu9/CV8AsvE5ddgOYYbNk+SMCjznESZcMg1KRzbdawnOklzloc+Q0iCxQK7 +022ukVxFbmT7U1hTVOTOzrruqBxptPDiutkKfOXebzYyZodlFFL5MWcatCWS3XL5 +1KBIeKWny5mExZPzIf1ofGuJe0zxllw8olgMqQIDAQABAoIBAB6x2DO/cpURbjZr +9lqsrErGirDVoze5GfM5tVMa0cHXQ0g9TiXH+X7TfqhE4+38qC02M6SFbzfDl4db +ahdb/1ezj5ivgmDpYcHmnhVUKX/0BCa87L3+a8+MYRsm9ppL66iKJJeLxyRM1b/u +mKyhCnwiW2hOnpbWAwtDieD0qDx0kzkYLevG3MivaAe1rDD6gS2LhPy6tGtmemRv +uT03a1X9DEO0U45oDvhi0V6dm8jz/eBBybHt0WWNNi2c5PVjhIL3HfT2UMiNa0xf +O3g1eGc8bdVPW4z0Kz1g+H/uvguTEqKVq4yT8Y2LuF0Vm2MxptqAmxxJTyL5q0ZI +V7PDg0ECgYEAziWa0BWbX+cMfIJXNi89aMpgBqBLdvQt9rE/TGjVeViPBcG8AoRr +Osxzct6tMd+DQO3uzx5DES2FiM7gbOtpGKnLWjJImmCq2gPCtWXo74I8il5egJNh +vq03eP7wdcm+bFta4+ScGv2wAbx28y3+5fQZPqzUISz+2dM8Hxx9GO0CgYEA8Hs0 +ap+FKD/nbRFfYMVLeP0WfJYuSpn9MF8FT73RSvCva4Ql0a3WbaZ5AMgLFGjm5nRp +idf5vPMaXbleDG1xZnkhhBXidwFmn4TCVvG/fiDjXOULuJ5qjKLv+5dTG72GDHGG +onNUwn1LQ3bJpGZ3VHIFJXOcQHl2Dxa6Cn7G9y0CgYBn0gyL67XasNRbCJG/mj8F +PZbq/2PCPuu/KDlG1C1e9bjiH1X+to4CiOFD4t27FmRWGP6ClS0Vw6VS502jzVOa +tjjR7i0egrzJG8e979tGdILk9O4HNzKtAzPC3jJgQACFNeUqjQIJneY8mZwWkP2k +9jCYnhYftzeKoJXQ3VoraQKBgQDiprxYYdDWhqRQH7eNNWZUufSfp8wpc8k19djD +t1uzDfXHl90tKnKXFfelzOTkb5pwSffOe0hd1aJcA4GopN3kfvYfz6CKGT/nyPCB +kYeyEL05qIbLkkNKGaelsJIb6xyUTctfAOQ6Cm0NQL/7urdtV6mSCsyR1+h1gC4I +BkTwYQKBgC7s9J9rRT23bx0NKyyHKu7/akAdC8m0YCohU5L7NNw0UZql0p8EQguh +bKhNO4+rlwh2VzIi5tVMQTYoUbaab8n17fdNxtfTsG0h4vj8q+7ab59GYf1TKn0R +JEn/NS0gRKfNh6bwZaSTfhFxALmKApVNTPm2UT9G5hADTcw4xTQf +-----END RSA PRIVATE KEY-----`; + +export const jwksResponse = { + keys: [ + { + alg: 'RS256', + kty: 'RSA', + use: 'sig', + n: 'waZ3afW0_zYy3HfJwAAr83PDdZvADuSJ6jTZk1-jprdHdG6PzH9XaB6xhzvwTIJFcWuREkNSC06MDLCuvmZ8fj93FcNaZ2ZJ0LFvY4SODMDqFekE5vD2Y15aSI2Y69qwKlVLphvEEXJ_FRqIHQX9wwCtwVsnqcLt_f5aNWRHyk2jwhz7IBm-dLu9_CV8AsvE5ddgOYYbNk-SMCjznESZcMg1KRzbdawnOklzloc-Q0iCxQK7022ukVxFbmT7U1hTVOTOzrruqBxptPDiutkKfOXebzYyZodlFFL5MWcatCWS3XL51KBIeKWny5mExZPzIf1ofGuJe0zxllw8olgMqQ', //eslint-disable-line max-len + e: 'AQAB', + kid: '0', + }, + ], +}; + +export const getToken = (scope: string, outerSub?: string) => { + const payload: any = { + scope, + }; + + if (scope === dashboardScopes) { + payload.sub = sub; + } else if (scope === walletScopes) { + payload.sub = sub2; + } + + if (outerSub) { + payload.sub = outerSub; + } + + const options = { + header: { kid: '0' }, + algorithm: 'RS256', + expiresIn: '1d', + issuer: AUTH_URL, + }; + + let token; + try { + token = jwt.sign(payload, privateKey, options); + } catch (err) { + console.log(err); + throw err; + } + + return `Bearer ${token}`; +}; diff --git a/apps/api/src/app/util/jest/mock.ts b/apps/api/src/app/util/jest/mock.ts new file mode 100644 index 000000000..8f8f7dafa --- /dev/null +++ b/apps/api/src/app/util/jest/mock.ts @@ -0,0 +1,78 @@ +import nock from 'nock'; +import { + userEmail2, + clientId, + clientSecret, + registrationAccessToken, + requestUris, + userWalletAddress2, + account2, + sub2, + account, + sub, + userEmail, + userWalletAddress, + sub3, + account3, + sub4, + account4, +} from './constants'; +import { getToken, jwksResponse } from './jwt'; +import { AUTH_URL } from '@thxnetwork/api/config/secrets'; + +export function mockAuthPath(method: string, path: string, status: number, callback: any = {}) { + const n = nock(AUTH_URL).persist() as any; + return n[method](path).reply(status, callback); +} + +export function mockUrl(method: string, baseUrl: string, path: string, status: number, callback: any = {}) { + const n = nock(baseUrl).persist() as any; + return n[method](path).reply(status, callback); +} + +export function mockStart() { + mockClear(); + mockAuthPath('get', '/jwks', 200, jwksResponse); + mockAuthPath('post', '/token', 200, async () => { + return { + access_token: getToken('openid accounts:read accounts:write'), + }; + }); + mockAuthPath('post', '/reg', 201, { + client_id: clientId, + registration_access_token: registrationAccessToken, + }); + mockAuthPath('delete', `/reg/${clientId}?access_token=${registrationAccessToken}`, 204, {}); + mockAuthPath('get', `/reg/${clientId}?access_token=${registrationAccessToken}`, 200, { + client_id: clientId, + client_secret: clientSecret, + request_uris: requestUris, + }); + + // mockAuthPath('get', `https://local.auth.thx.network/account?subs=${sub}`, 200, account); + + // Account 1 (Dashboard) + mockAuthPath('get', `/accounts/${sub}`, 200, account); + mockAuthPath('patch', `/accounts/${sub}`, 204, {}); + mockAuthPath('get', `/accounts/email/${userEmail}`, 200, account); + mockAuthPath('get', `/accounts/address/${userWalletAddress}`, 200, account); + + // Account 2 (Web Wallet) + mockAuthPath('get', `/accounts/${sub2}`, 200, account2); + mockAuthPath('patch', `/accounts/${sub2}`, 204, {}); + mockAuthPath('post', '/accounts', 200, [account2]); + mockAuthPath('get', `/accounts/address/${userWalletAddress2}`, 200, account2); + mockAuthPath('get', `/accounts/email/${userEmail2}`, 404, {}); + + // Account 3 + mockAuthPath('get', `/accounts/${sub3}`, 200, account3); + mockAuthPath('patch', `/accounts/${sub3}`, 204, account3); + + // Account 4 + mockAuthPath('get', `/accounts/${sub4}`, 200, account4); + mockAuthPath('patch', `/accounts/${sub4}`, 204, account4); +} + +export function mockClear() { + return nock.cleanAll(); +} diff --git a/apps/api/src/app/util/jest/network.ts b/apps/api/src/app/util/jest/network.ts new file mode 100644 index 000000000..8c311da17 --- /dev/null +++ b/apps/api/src/app/util/jest/network.ts @@ -0,0 +1,57 @@ +import { ethers } from 'ethers'; +import { VOTER_PK, DEPOSITOR_PK } from './constants'; +import { getProvider } from '@thxnetwork/api/util/network'; +import { ChainId } from '@thxnetwork/common/enums'; +import { contractNetworks } from '@thxnetwork/api/hardhat'; +import { HARDHAT_RPC, SAFE_TXS_SERVICE } from '@thxnetwork/api/config/secrets'; +import Safe, { EthersAdapter } from '@safe-global/protocol-kit'; +import SafeApiKit from '@safe-global/api-kit'; + +const { web3 } = getProvider(ChainId.Hardhat); + +const voter = web3.eth.accounts.privateKeyToAccount(VOTER_PK) as any; +const depositor = web3.eth.accounts.privateKeyToAccount(DEPOSITOR_PK) as any; + +function createWallet(privateKey: string): any { + return web3.eth.accounts.privateKeyToAccount(privateKey); +} + +export const timeTravel = async (seconds: number) => { + web3.extend({ + methods: [ + { + name: 'increaseTime', + call: 'evm_increaseTime', + params: 1, + }, + { + name: 'mine', + call: 'evm_mine', + }, + ], + }); + await (web3 as any).increaseTime(seconds); +}; + +export const signMessage = (privateKey: string, message: string) => { + const wallet = createWallet(privateKey); + return wallet.sign(message).signature; +}; + +export async function signTxHash(safeAddress: string, safeTxHash: string, privateKey: string) { + const provider = new ethers.providers.JsonRpcProvider(HARDHAT_RPC); + const signer = new ethers.Wallet(privateKey, provider); + const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: signer as any }) as any; + const safe = await Safe.create({ + ethAdapter, + safeAddress, + contractNetworks, + }); + const signedTx = await safe.signTransactionHash(safeTxHash); + const apiKit = new SafeApiKit({ txServiceUrl: SAFE_TXS_SERVICE, ethAdapter }); + const { signature } = await apiKit.confirmTransaction(safeTxHash, signedTx.data); + + return { safeTxHash, signature }; +} + +export { voter, depositor, createWallet }; diff --git a/apps/api/src/app/util/jest/test.jpg b/apps/api/src/app/util/jest/test.jpg new file mode 100644 index 000000000..96a94a982 Binary files /dev/null and b/apps/api/src/app/util/jest/test.jpg differ diff --git a/apps/api/src/app/util/jwt.ts b/apps/api/src/app/util/jwt.ts new file mode 100644 index 000000000..63ba995e5 --- /dev/null +++ b/apps/api/src/app/util/jwt.ts @@ -0,0 +1,6 @@ +import jwt_decode from 'jwt-decode'; + +export const parseToken = (header: string): { sub: string } => { + if (!header || !header.startsWith('Bearer ')) return; + return jwt_decode(header.split(' ')[1]); +}; diff --git a/apps/api/src/app/util/logger.ts b/apps/api/src/app/util/logger.ts new file mode 100644 index 000000000..e43d81206 --- /dev/null +++ b/apps/api/src/app/util/logger.ts @@ -0,0 +1,14 @@ +import winston from 'winston'; +import { NODE_ENV } from '@thxnetwork/api/config/secrets'; + +const formatWinston = winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + winston.format.errors({ stack: true }), + winston.format.json(), +); + +export const logger = winston.createLogger({ + level: NODE_ENV === 'test' ? 'warn' : 'debug', + format: formatWinston, + transports: [new winston.transports.Console()], +}); diff --git a/apps/api/src/app/util/multer.ts b/apps/api/src/app/util/multer.ts new file mode 100644 index 000000000..5eefec84e --- /dev/null +++ b/apps/api/src/app/util/multer.ts @@ -0,0 +1,3 @@ +import multer from 'multer'; + +export const upload = multer({}); diff --git a/apps/api/src/app/util/network.ts b/apps/api/src/app/util/network.ts new file mode 100644 index 000000000..c348c585f --- /dev/null +++ b/apps/api/src/app/util/network.ts @@ -0,0 +1,99 @@ +import { + HARDHAT_RPC, + POLYGON_RELAYER, + POLYGON_RELAYER_API_KEY, + POLYGON_RELAYER_API_SECRET, + POLYGON_RPC, + PRIVATE_KEY, + RELAYER_SPEED, + SAFE_TXS_SERVICE, +} from '@thxnetwork/api/config/secrets'; +import Web3 from 'web3'; +import { ethers, Signer, Wallet } from 'ethers'; +import { Contract } from 'web3-eth-contract'; +import { recoverAddress, hashMessage } from 'ethers/lib/utils'; +import { EthersAdapter } from '@safe-global/protocol-kit'; +import { DefenderRelaySigner } from '@openzeppelin/defender-relay-client/lib/ethers'; +import { Relayer } from '@openzeppelin/defender-relay-client'; +import { DefenderRelayProvider } from '@openzeppelin/defender-relay-client/lib/web3'; +import { ChainId } from '@thxnetwork/common/enums'; +import ContractService from '@thxnetwork/api/services/ContractService'; + +export const MaxUint256 = '115792089237316195423570985008687907853269984665640564039457584007913129639935'; + +const web3 = new Web3(); +const networks: { + [chainId: number]: { + web3: Web3; + txServiceUrl: string; + signer: Signer; + ethAdapter: any; + defaultAccount: string; + relayer?: Relayer; + }; +} = {}; + +if (HARDHAT_RPC) { + networks[ChainId.Hardhat] = (() => { + const web3 = new Web3(HARDHAT_RPC); + const signer = new Wallet(PRIVATE_KEY, new ethers.providers.JsonRpcProvider(HARDHAT_RPC)); + const methods = [ + { name: 'setAutomine', call: 'evm_setAutomine', params: 1 }, + { name: 'setIntervalMining', call: 'evm_setIntervalMining', params: 1 }, + ]; + web3.extend({ property: 'hardhat', methods }); + return { + web3, + txServiceUrl: SAFE_TXS_SERVICE, + ethAdapter: new EthersAdapter({ ethers, signerOrProvider: signer as any }), + signer, + defaultAccount: web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY).address, + }; + })(); +} + +if (POLYGON_RELAYER) { + networks[ChainId.Polygon] = (() => { + const provider = new DefenderRelayProvider( + { apiKey: POLYGON_RELAYER_API_KEY, apiSecret: POLYGON_RELAYER_API_SECRET }, + { speed: RELAYER_SPEED }, + ); + const relayer = new Relayer({ apiKey: POLYGON_RELAYER_API_KEY, apiSecret: POLYGON_RELAYER_API_SECRET }); + const signer = new DefenderRelaySigner( + { apiKey: POLYGON_RELAYER_API_KEY, apiSecret: POLYGON_RELAYER_API_SECRET }, + new ethers.providers.JsonRpcProvider(POLYGON_RPC), + { speed: RELAYER_SPEED }, + ) as unknown as Signer; + + return { + web3: new Web3(provider), + txServiceUrl: SAFE_TXS_SERVICE, + ethAdapter: new EthersAdapter({ + ethers, + signerOrProvider: signer as any, + }), + signer, + relayer, + defaultAccount: POLYGON_RELAYER, + }; + })(); +} + +export function getProvider(chainId?: ChainId) { + if (!chainId) chainId = ContractService.getChainId(); + if (!networks[chainId]) throw new Error(`Network with chainId ${chainId} is not available`); + return networks[chainId]; +} + +export const recoverSigner = (message: string, sig: string) => { + return recoverAddress(hashMessage(message), sig); +}; + +export function getSelectors(contract: Contract) { + const signatures: string[] = []; + for (const sig of Object.keys(contract.methods)) { + if (sig.indexOf('(') === -1) continue; // Only add selectors for full function signatures. + signatures.push(web3.eth.abi.encodeFunctionSignature(sig)); + } + return signatures; +} diff --git a/apps/api/src/app/util/newrelic.ts b/apps/api/src/app/util/newrelic.ts new file mode 100644 index 000000000..4f3beb035 --- /dev/null +++ b/apps/api/src/app/util/newrelic.ts @@ -0,0 +1,12 @@ +import newrelic from 'newrelic'; + +export const wrapBackgroundTransaction = (name: string, group: string, promise: Promise<unknown>): Promise<unknown> => { + return newrelic.startBackgroundTransaction(name, group, async () => { + try { + return await promise; + } catch (error) { + newrelic.noticeError(error); + throw error; + } + }); +}; diff --git a/apps/api/src/app/util/pagination.ts b/apps/api/src/app/util/pagination.ts new file mode 100644 index 000000000..62c9c9ed4 --- /dev/null +++ b/apps/api/src/app/util/pagination.ts @@ -0,0 +1,45 @@ +export interface PaginationResult { + results: any[]; + next?: { page: number }; + previous?: { page: number }; + limit: number; + total: number; +} + +export const paginatedResults = async ( + model: any, + page: number, + limit: number, + query: any, + sorts: any = [['createdAt', -1]], +): Promise<PaginationResult> => { + const startIndex = (page - 1) * limit; + const endIndex = page * limit; + const total = await model.find(query).countDocuments().exec(); + let next, previous; + + if (endIndex < total) { + next = { + next: { + page: page + 1, + }, + }; + } + if (startIndex > 0) { + previous = { + previous: { + page: page - 1, + }, + }; + } + + const results = await model.find(query).sort(sorts).limit(limit).skip(startIndex).exec(); + + return { + results, + limit, + total, + ...next, + ...previous, + }; +}; diff --git a/apps/api/src/app/util/path.ts b/apps/api/src/app/util/path.ts new file mode 100644 index 000000000..98377c9a6 --- /dev/null +++ b/apps/api/src/app/util/path.ts @@ -0,0 +1,4 @@ +import path from 'path'; +import { CWD } from '../config/secrets'; + +export const assetsPath = path.resolve(CWD, 'assets'); diff --git a/apps/api/src/app/util/polling.ts b/apps/api/src/app/util/polling.ts new file mode 100644 index 000000000..87f5adc8e --- /dev/null +++ b/apps/api/src/app/util/polling.ts @@ -0,0 +1,14 @@ +export async function poll<T>(fn: () => Promise<T>, fnCondition: (result: T) => boolean, ms: number) { + let result = await fn(); + while (fnCondition(result)) { + await wait(ms); + result = await fn(); + } + return result; +} + +function wait(ms = 1000) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} diff --git a/apps/api/src/app/util/random.ts b/apps/api/src/app/util/random.ts new file mode 100644 index 000000000..2179cba15 --- /dev/null +++ b/apps/api/src/app/util/random.ts @@ -0,0 +1,3 @@ +export function generateRandomString(length: number) { + return (+new Date() * Math.random()).toString(36).substring(0, length); +} diff --git a/apps/api/src/app/util/ratelimiter.ts b/apps/api/src/app/util/ratelimiter.ts new file mode 100644 index 000000000..4900c2131 --- /dev/null +++ b/apps/api/src/app/util/ratelimiter.ts @@ -0,0 +1,6 @@ +import rateLimit from 'express-rate-limit'; +import { NODE_ENV } from '../config/secrets'; + +const limitInSeconds = (seconds: number) => rateLimit({ windowMs: NODE_ENV !== 'test' && seconds * 1000, max: 1 }); + +export { limitInSeconds }; diff --git a/apps/api/src/app/util/s3.ts b/apps/api/src/app/util/s3.ts new file mode 100644 index 000000000..57a6c78b5 --- /dev/null +++ b/apps/api/src/app/util/s3.ts @@ -0,0 +1,26 @@ +import { S3, S3Client } from '@aws-sdk/client-s3'; +import { + AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY, + AWS_S3_PRIVATE_BUCKET_REGION, + AWS_S3_PUBLIC_BUCKET_REGION, +} from '@thxnetwork/api/config/secrets'; + +const credentials = { + accessKeyId: AWS_ACCESS_KEY_ID, + secretAccessKey: AWS_SECRET_ACCESS_KEY, +}; + +const s3Client = new S3Client({ + region: AWS_S3_PUBLIC_BUCKET_REGION, + credentials, +}); + +const s3PrivateClient = new S3Client({ + region: AWS_S3_PRIVATE_BUCKET_REGION, + credentials, +}); + +const s3Public = new S3({ region: AWS_S3_PUBLIC_BUCKET_REGION, credentials }); + +export { s3Public, s3Client, s3PrivateClient }; diff --git a/apps/api/src/app/util/scopes.ts b/apps/api/src/app/util/scopes.ts new file mode 100644 index 000000000..024bb03b7 --- /dev/null +++ b/apps/api/src/app/util/scopes.ts @@ -0,0 +1,63 @@ +export const openId = 'openid'; +export const adminScopes = [ + 'account:read', + 'account:write', + 'members:read', + 'members:write', + 'withdrawals:write', + 'rewards:read', + 'wallets:read', + 'wallets:write', + 'erc20_rewards:read', + 'erc721_rewards:read', + 'referral_rewards:read', +]; +export const dashboardScopes = [ + 'asset_pools:read', + 'asset_pools:write', + 'rewards:read', + 'rewards:write', + 'deposits:read', + 'deposits:write', + 'promotions:read', + 'promotions:write', + 'transactions:read', + 'claims:read', + 'swaprule:read', + 'swaprule:write', + 'erc20_rewards:read', + 'erc20_rewards:write', + 'erc721_rewards:read', + 'erc721_rewards:write', + 'referral_rewards:read', + 'referral_rewards:write', + 'pool_subscription:read', +]; +export const userScopes = [ + 'asset_pools:read', + 'asset_pools:write', + 'rewards:read', + 'withdrawals:read', + 'deposits:read', + 'deposits:write', + 'transactions:read', + 'transactions:write', + 'claims:read', + 'swaprule:read', + 'swap:read', + 'swap:write', + 'erc20_rewards:read', + 'erc721_rewards:read', + 'referral_rewards:read', + 'referal_reward_claims:read', + 'referal_reward_claims:write', +]; + +export const opneIdAdminScopes = `${openId} ${adminScopes.join(' ')}`; +export const openIdDashboardScopes = `${openId} ${dashboardScopes.join(' ')}`; +export const openIdUserScopes = `${openId} ${userScopes.join(' ')}`; + +export const adminDashboardScopes = Array.from(new Set([...adminScopes, ...dashboardScopes])); +export const userDashboardScopes = Array.from(new Set([...userScopes, ...dashboardScopes])); +export const userAdminScopes = Array.from(new Set([...adminScopes, ...userScopes])); +export const userAdminDashboardScopes = Array.from(new Set([...adminScopes, ...userScopes, ...dashboardScopes])); diff --git a/apps/api/src/app/util/signingsecret.ts b/apps/api/src/app/util/signingsecret.ts new file mode 100644 index 000000000..736149ad2 --- /dev/null +++ b/apps/api/src/app/util/signingsecret.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; + +export function getsigningSecret(length: number) { + return crypto.randomBytes(length).toString('base64'); +} + +export function signPayload(payload: string, secret: string): string { + const hmac = crypto.createHmac('sha256', secret); + hmac.update(payload); + return hmac.digest('base64'); +} diff --git a/apps/api/src/app/util/token.ts b/apps/api/src/app/util/token.ts new file mode 100644 index 000000000..786573023 --- /dev/null +++ b/apps/api/src/app/util/token.ts @@ -0,0 +1,6 @@ +import crypto from 'crypto'; + +export function createRandomToken() { + const buf = crypto.randomBytes(16); + return buf.toString('hex'); +} diff --git a/apps/api/src/app/util/twitter.ts b/apps/api/src/app/util/twitter.ts new file mode 100644 index 000000000..ea07f182a --- /dev/null +++ b/apps/api/src/app/util/twitter.ts @@ -0,0 +1,8 @@ +import { TWITTER_API_TOKEN } from '@thxnetwork/api/config/secrets'; +import axios, { AxiosRequestConfig } from 'axios'; + +export function twitterClient(config: AxiosRequestConfig) { + axios.defaults.headers['Authorization'] = `Bearer ${TWITTER_API_TOKEN}`; + axios.defaults.baseURL = 'https://api.twitter.com/2'; + return axios(config); +} diff --git a/apps/api/src/app/util/url.ts b/apps/api/src/app/util/url.ts new file mode 100644 index 000000000..4b292f77d --- /dev/null +++ b/apps/api/src/app/util/url.ts @@ -0,0 +1,2 @@ +const URL_REGEX = /^(https?):\/\/[^\s/$.?#].[^\s]*$/i; +export const isValidUrl = (url: string) => url.match(URL_REGEX); diff --git a/apps/api/src/app/util/uuid.ts b/apps/api/src/app/util/uuid.ts new file mode 100644 index 000000000..b50ed5c80 --- /dev/null +++ b/apps/api/src/app/util/uuid.ts @@ -0,0 +1,23 @@ +import crypto from 'crypto'; +import { v1 } from 'uuid'; + +export function uuidV1(salt?: string) { + if (!salt) return v1(); + + // Use a cryptographic hash function (SHA-256 in this example) + const hash = crypto.createHash('sha256'); + + // Update the hash with the salt + hash.update(salt); + + // Get the hashed data in hexadecimal format + const hashedData = hash.digest('hex'); + + // Create a UUID from the first 16 bytes of the hashed data + const uuid = `${hashedData.slice(0, 8)}-${hashedData.slice(8, 12)}-${hashedData.slice(12, 16)}-${hashedData.slice( + 16, + 20, + )}-${hashedData.slice(20, 32)}`; + + return uuid; +} diff --git a/apps/api/src/app/util/validation.ts b/apps/api/src/app/util/validation.ts new file mode 100644 index 000000000..4b6f306c5 --- /dev/null +++ b/apps/api/src/app/util/validation.ts @@ -0,0 +1,65 @@ +import { body, check, validationResult } from 'express-validator'; +import { Response, Request, NextFunction } from 'express'; +import { isValidUrl } from './url'; + +export const validate = (validations: any) => { + return async (req: Request, res: Response, next: NextFunction) => { + await Promise.all(validations.map((validation: any) => validation.run(req))); + const errors = validationResult(req); + if (errors.isEmpty()) return next(); + res.status(400).json({ errors: errors.array() }); + }; +}; + +export const defaults = { + quest: [ + body('index').optional().isInt(), + body('title').optional().isString().trim().escape(), + body('description').optional().isString().trim().escape(), + body('expiryDate').optional().isISO8601(), + body('isPublished') + .optional() + .isBoolean() + .customSanitizer((value) => JSON.parse(value)), + check('file') + .optional() + .custom((value, { req }) => { + return ['jpg', 'jpeg', 'gif', 'png'].includes(req.file.mimetype); + }), + body('infoLinks') + .optional() + .customSanitizer((infoLinks) => { + return JSON.parse(infoLinks).filter((link: TInfoLink) => link.label.length && isValidUrl(link.url)); + }), + body('locks') + .optional() + .custom((value) => { + const locks = value && JSON.parse(value); + return Array.isArray(locks); + }) + .customSanitizer((locks) => locks && JSON.parse(locks)), + ], + reward: [ + body('title').optional().isString().trim().escape(), + body('description').optional().isString().trim().escape(), + body('expiryDate').optional().isISO8601(), + body('limit').optional().isNumeric(), + body('pointPrice').optional().isNumeric(), + body('isPublished') + .optional() + .isBoolean() + .customSanitizer((value) => JSON.parse(value)), + check('file') + .optional() + .custom((value) => { + return ['jpg', 'jpeg', 'gif', 'png'].includes(value.mimetype); + }), + body('locks') + .optional() + .custom((value) => { + const locks = value && JSON.parse(value); + return Array.isArray(locks); + }) + .customSanitizer((locks) => locks && JSON.parse(locks)), + ], +}; diff --git a/apps/api/src/app/util/zip.ts b/apps/api/src/app/util/zip.ts new file mode 100644 index 000000000..28c845e99 --- /dev/null +++ b/apps/api/src/app/util/zip.ts @@ -0,0 +1,9 @@ +import JSZip from 'jszip'; + +export const createArchiver = () => { + const jsZip = new JSZip(); + return { + jsZip, + archive: jsZip.folder('qrcodes'), + }; +}; diff --git a/apps/api/src/assets/.gitkeep b/apps/api/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/api/src/assets/bg.png b/apps/api/src/assets/bg.png new file mode 100644 index 000000000..e58c94831 Binary files /dev/null and b/apps/api/src/assets/bg.png differ diff --git a/apps/api/src/assets/fa-solid-900.ttf b/apps/api/src/assets/fa-solid-900.ttf new file mode 100644 index 000000000..e6330e6aa Binary files /dev/null and b/apps/api/src/assets/fa-solid-900.ttf differ diff --git a/apps/campaign/src/assets/logo.png b/apps/api/src/assets/logo.png similarity index 100% rename from apps/campaign/src/assets/logo.png rename to apps/api/src/assets/logo.png diff --git a/apps/api/src/assets/qr-logo.jpg b/apps/api/src/assets/qr-logo.jpg new file mode 100644 index 000000000..96a94a982 Binary files /dev/null and b/apps/api/src/assets/qr-logo.jpg differ diff --git a/apps/api/src/assets/views/email/base-template.ejs b/apps/api/src/assets/views/email/base-template.ejs new file mode 100644 index 000000000..ed8f7ba46 --- /dev/null +++ b/apps/api/src/assets/views/email/base-template.ejs @@ -0,0 +1,407 @@ +<!DOCTYPE html> +<html> + +<head> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title><%= subject %></title> + <style> + /* ------------------------------------- + GLOBAL RESETS + ------------------------------------- */ + + img { + border: none; + -ms-interpolation-mode: bicubic; + max-width: 100%; + } + + body { + background-color: #f6f6f6; + font-family: sans-serif; + -webkit-font-smoothing: antialiased; + font-size: 14px; + line-height: 1.4; + margin: 0; + padding: 0; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + } + + table { + border-collapse: separate; + mso-table-lspace: 0pt; + mso-table-rspace: 0pt; + width: 100%; + } + + table td { + font-family: sans-serif; + font-size: 14px; + vertical-align: top; + } + + /* ------------------------------------- + BODY & CONTAINER + ------------------------------------- */ + + .body { + background-color: #f6f6f6; + width: 100%; + } + + /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ + .container { + display: block; + margin: 0 auto !important; + /* makes it centered */ + max-width: 580px; + padding: 10px; + width: 580px; + } + + /* This should also be a block element, so that it will fill 100% of the .container */ + .content { + box-sizing: border-box; + display: block; + margin: 0 auto; + max-width: 580px; + padding: 10px; + } + + /* ------------------------------------- + HEADER, FOOTER, MAIN + ------------------------------------- */ + .main { + background: #ffffff; + border-radius: 3px; + width: 100%; + } + + .wrapper { + box-sizing: border-box; + padding: 20px; + } + + .content-block { + padding-bottom: 10px; + padding-top: 10px; + } + + .footer { + clear: both; + margin-top: 10px; + text-align: center; + width: 100%; + } + + .footer td, + .footer p, + .footer span, + .footer a { + color: #999999; + font-size: 12px; + text-align: center; + } + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1, + h2, + h3, + h4 { + color: #000000; + font-family: sans-serif; + font-weight: 400; + line-height: 1.4; + margin: 0; + margin-bottom: 30px; + } + + h1 { + font-size: 35px; + font-weight: 300; + text-align: center; + text-transform: capitalize; + } + + p, + ul, + ol { + font-family: sans-serif; + font-size: 16px; + font-weight: normal; + margin: 0; + margin-bottom: 15px; + } + + p li, + ul li, + ol li { + list-style-position: inside; + margin-left: 5px; + } + + a { + color: #3498db; + text-decoration: underline; + } + + /* ------------------------------------- + BUTTONS + ------------------------------------- */ + .btn { + box-sizing: border-box; + width: 100%; + } + + .btn>tbody>tr>td { + padding-bottom: 15px; + } + + .btn table { + width: auto; + } + + .btn table td { + background-color: #ffffff; + border-radius: 5px; + text-align: center; + } + + .btn a { + display: block; + font-size: 16px !important; + width: 100%; + text-align: center; + color: #ffffff; + background-color: #5942c1; + border: 0; + border-radius: 50rem; + box-sizing: border-box; + cursor: pointer; + text-decoration: none; + font-size: 12px; + margin: 0; + padding: 12px 25px; + } + + .btn-primary table td { + background-color: #3498db; + } + + .btn-primary a { + background-color: #3498db; + border-color: #3498db; + color: #ffffff; + } + + /* ------------------------------------- + OTHER STYLES THAT MIGHT BE USEFUL + ------------------------------------- */ + .last { + margin-bottom: 0; + } + + .first { + margin-top: 0; + } + + .align-center { + text-align: center; + } + + .align-right { + text-align: right; + } + + .align-left { + text-align: left; + } + + .clear { + clear: both; + } + + .mt0 { + margin-top: 0; + } + + .mb0 { + margin-bottom: 0; + } + + .preheader { + color: transparent; + display: none; + height: 0; + max-height: 0; + max-width: 0; + opacity: 0; + overflow: hidden; + mso-hide: all; + visibility: hidden; + width: 0; + } + + .powered-by a { + text-decoration: none; + } + + hr { + border: 0; + border-bottom: 1px solid #f6f6f6; + margin: 20px 0; + } + + /* ------------------------------------- + RESPONSIVE AND MOBILE FRIENDLY STYLES + ------------------------------------- */ + @media only screen and (max-width: 620px) { + table.body h1 { + font-size: 28px !important; + margin-bottom: 10px !important; + } + + table.body p, + table.body ul, + table.body ol, + table.body td, + table.body span, + table.body a { + font-size: 16px !important; + } + + table.body .wrapper, + table.body .article { + padding: 10px !important; + } + + table.body .content { + padding: 0 !important; + } + + table.body .container { + padding: 0 !important; + width: 100% !important; + } + + table.body .main { + border-left-width: 0 !important; + border-radius: 0 !important; + border-right-width: 0 !important; + } + + table.body .btn table { + width: 100% !important; + } + + table.body .btn a { + width: 100% !important; + } + + table.body .img-responsive { + height: auto !important; + max-width: 100% !important; + width: auto !important; + } + } + + /* ------------------------------------- + PRESERVE THESE STYLES IN THE HEAD + ------------------------------------- */ + @media all { + .ExternalClass { + width: 100%; + } + + .ExternalClass, + .ExternalClass p, + .ExternalClass span, + .ExternalClass font, + .ExternalClass td, + .ExternalClass div { + line-height: 100%; + } + + .apple-link a { + color: inherit !important; + font-family: inherit !important; + font-size: inherit !important; + font-weight: inherit !important; + line-height: inherit !important; + text-decoration: none !important; + } + + #MessageViewBody a { + color: inherit; + text-decoration: none; + font-size: inherit; + font-family: inherit; + font-weight: inherit; + line-height: inherit; + } + + .btn-primary table td:hover { + background-color: #34495e !important; + } + + .btn-primary a:hover { + background-color: #34495e !important; + border-color: #34495e !important; + } + } + </style> +</head> + +<body> + <span class="preheader"><%- htmlContent %></span> + <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"> + <tr> + <td> </td> + <td class="container"> + <div class="content"> + <!-- START CENTERED WHITE CONTAINER --> + <table role="presentation" class="main"> + <!-- START MAIN CONTENT AREA --> + <tr> + <td class="wrapper"> + <table role="presentation" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td> + <%- htmlContent %> + <% if (link.src && link.text) { %> + <div class="btn"> + <a href="<%= link.src %>" target="_blank"><%= link.text %></a> + </div> + <% } %> + </td> + </tr> + </table> + </td> + </tr> + + <!-- END MAIN CONTENT AREA --> + </table> + <!-- END CENTERED WHITE CONTAINER --> + + <!-- START FOOTER --> + <div class="footer"> + <table role="presentation" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td class="content-block powered-by"> + <img src="<%= baseUrl %>/img/logo.png" width="60" alt="THX Logo"> + </td> + </tr> + </table> + </div> + <!-- END FOOTER --> + </div> + </td> + <td> </td> + </tr> + </table> +</body> + +</html> \ No newline at end of file diff --git a/apps/api/src/discord.ts b/apps/api/src/discord.ts new file mode 100644 index 000000000..400977fd0 --- /dev/null +++ b/apps/api/src/discord.ts @@ -0,0 +1,21 @@ +import { Client, GatewayIntentBits, Partials, PermissionFlagsBits } from 'discord.js'; +import { BOT_TOKEN } from '@thxnetwork/api/config/secrets'; +import { eventRegister } from '@thxnetwork/api/util/discord'; +import { logger } from './app/util/logger'; +import eventRouter from '@thxnetwork/api/events'; + +export const client = new Client({ + intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions], + partials: [Partials.Message, Partials.Channel, Partials.Reaction], +}); + +export default async () => { + try { + eventRegister(client, eventRouter); + client.login(BOT_TOKEN); + } catch (error) { + logger.error(error); + } +}; + +export { PermissionFlagsBits }; diff --git a/apps/api/src/environments/environment.prod.ts b/apps/api/src/environments/environment.prod.ts new file mode 100644 index 000000000..c9669790b --- /dev/null +++ b/apps/api/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/api/src/environments/environment.ts b/apps/api/src/environments/environment.ts new file mode 100644 index 000000000..a20cfe557 --- /dev/null +++ b/apps/api/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +}; diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts new file mode 100644 index 000000000..cf031b58f --- /dev/null +++ b/apps/api/src/main.ts @@ -0,0 +1,71 @@ +import 'newrelic'; +import http from 'http'; +import https from 'https'; +import httpProxy from 'http-proxy'; +import app from './app'; +import discordBot from './discord'; +import db from './app/util/database'; +import { createTerminus } from '@godaddy/terminus'; +import { healthCheck } from './app/util/healthcheck'; +import { logger } from './app/util/logger'; +import { agenda } from './app/util/agenda'; +import fs from 'fs'; +import { LOCAL_CERT, LOCAL_CERT_KEY, NODE_ENV } from './app/config/secrets'; +import path from 'path'; + +let server: http.Server; + +if (LOCAL_CERT && LOCAL_CERT_KEY) { + const ssl = { + key: fs.readFileSync(path.resolve(path.dirname(__dirname), LOCAL_CERT_KEY)), + cert: fs.readFileSync(path.resolve(path.dirname(__dirname), LOCAL_CERT)), + }; + server = https.createServer(ssl, app); + httpProxy + .createProxyServer({ + target: { + host: '127.0.0.1', + port: 8545, + }, + ssl, + }) + .listen(8547); +} else { + server = http.createServer(app); +} + +const options = { + healthChecks: { + '/healthcheck': healthCheck, + 'verbatim': true, + }, + onSignal: () => { + logger.info('Server shutting down gracefully'); + return Promise.all([db.disconnect(), agenda.stop()]); + }, + logger: logger.error, +}; + +createTerminus(server, options); + +process.on('uncaughtException', function (err: Error) { + if (err) { + logger.error({ + message: 'Uncaught Exception was thrown, shutting down', + errorName: err.name, + errorMessage: err.message, + stack: err.stack, + }); + process.exit(1); + } +}); + +logger.info({ + message: `Server is starting on port: ${app.get('port')}, env: ${NODE_ENV}`, + port: app.get('port'), + env: NODE_ENV, +}); + +server.listen(app.get('port')); + +discordBot(); diff --git a/apps/api/tsconfig.app.json b/apps/api/tsconfig.app.json new file mode 100644 index 000000000..12fae6ea6 --- /dev/null +++ b/apps/api/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "../../libs/common/src/lib/scss/**/*"], + "include": ["src/**/*.ts", "../../libs/common/src/**/*", "../../libs/sdk/src/**/*", "src/app/hardhat/export/*.json"] +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 000000000..d2b6e8e03 --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + // Custom + "noImplicitAny": false, + "strict": false + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/api/tsconfig.spec.json b/apps/api/tsconfig.spec.json new file mode 100644 index 000000000..5692087fb --- /dev/null +++ b/apps/api/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + // Custom + "allowJs": true + }, + "exclude": ["../../libs/common/src/lib/scss/**/*"], + "include": [ + "jest.config.ts", + "**/*.test.ts", + "**/*.d.ts", + "src/**/*.ts", + "../../libs/common/src/**/*", + "../../libs/sdk/src/**/*", + "src/app/hardhat/export/*.json" + ] +} diff --git a/apps/api/webpack.config.js b/apps/api/webpack.config.js new file mode 100644 index 000000000..29f653d71 --- /dev/null +++ b/apps/api/webpack.config.js @@ -0,0 +1,13 @@ +const { composePlugins, withNx } = require('@nx/webpack'); + +// Nx plugins for webpack. +module.exports = composePlugins( + withNx({ + target: 'node', + }), + (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + return config; + }, +); diff --git a/apps/campaign/.build.env b/apps/app/.build.env similarity index 100% rename from apps/campaign/.build.env rename to apps/app/.build.env diff --git a/apps/campaign/.env.example b/apps/app/.env.example similarity index 100% rename from apps/campaign/.env.example rename to apps/app/.env.example diff --git a/apps/campaign/.eslintrc.json b/apps/app/.eslintrc.json similarity index 100% rename from apps/campaign/.eslintrc.json rename to apps/app/.eslintrc.json diff --git a/apps/app/.gitkeep b/apps/app/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/campaign/components.d.ts b/apps/app/components.d.ts similarity index 100% rename from apps/campaign/components.d.ts rename to apps/app/components.d.ts diff --git a/apps/campaign/index.html b/apps/app/index.html similarity index 100% rename from apps/campaign/index.html rename to apps/app/index.html diff --git a/apps/campaign/project.json b/apps/app/project.json similarity index 79% rename from apps/campaign/project.json rename to apps/app/project.json index 1d8bc7951..4b9c0967f 100644 --- a/apps/campaign/project.json +++ b/apps/app/project.json @@ -1,20 +1,21 @@ { - "name": "campaign", + "name": "app", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/campaign/src", + "sourceRoot": "apps/app/src", "projectType": "application", + "tags": [], "targets": { "build": { "executor": "@nx/vite:build", "options": { - "outputPath": "dist/apps/campaign" + "outputPath": "dist/apps/app" } }, "serve": { "executor": "@nx/vite:dev-server", "defaultConfiguration": "development", "options": { - "buildTarget": "campaign:build", + "buildTarget": "app:build", "port": 8080 }, "configurations": {} @@ -34,9 +35,8 @@ "test": { "executor": "@nxext/vitest:vitest", "options": { - "vitestConfig": "apps/campaign/vitest.config.ts" + "vitestConfig": "apps/app/vitest.config.ts" } } - }, - "tags": [] + } } diff --git a/apps/campaign/public/favicon.ico b/apps/app/public/favicon.ico similarity index 100% rename from apps/campaign/public/favicon.ico rename to apps/app/public/favicon.ico diff --git a/apps/campaign/public/notify.js b/apps/app/public/notify.js similarity index 84% rename from apps/campaign/public/notify.js rename to apps/app/public/notify.js index 8c4a55f4f..508d6f05e 100644 --- a/apps/campaign/public/notify.js +++ b/apps/app/public/notify.js @@ -3,7 +3,7 @@ const args = process.argv.slice(2); const [branch, version, webhook] = args; if (!branch || !version || !webhook) { - console.error('Usage: nx run campaign:notify --branch=:branch --version=:version --webhook=:webhook'); + console.error('Usage: nx run app:notify --branch=:branch --version=:version --webhook=:webhook'); process.exit(0); } diff --git a/apps/campaign/public/signin-popup.html b/apps/app/public/signin-popup.html similarity index 100% rename from apps/campaign/public/signin-popup.html rename to apps/app/public/signin-popup.html diff --git a/apps/campaign/public/signin-silent.html b/apps/app/public/signin-silent.html similarity index 100% rename from apps/campaign/public/signin-silent.html rename to apps/app/public/signin-silent.html diff --git a/apps/campaign/public/signout-popup.html b/apps/app/public/signout-popup.html similarity index 100% rename from apps/campaign/public/signout-popup.html rename to apps/app/public/signout-popup.html diff --git a/apps/campaign/src/App.vue b/apps/app/src/App.vue similarity index 100% rename from apps/campaign/src/App.vue rename to apps/app/src/App.vue diff --git a/apps/app/src/assets/.gitkeep b/apps/app/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/campaign/src/assets/bg-overlay.png b/apps/app/src/assets/bg-overlay.png similarity index 100% rename from apps/campaign/src/assets/bg-overlay.png rename to apps/app/src/assets/bg-overlay.png diff --git a/apps/campaign/src/assets/bg-planet.png b/apps/app/src/assets/bg-planet.png similarity index 100% rename from apps/campaign/src/assets/bg-planet.png rename to apps/app/src/assets/bg-planet.png diff --git a/apps/campaign/src/assets/discord-logo.png b/apps/app/src/assets/discord-logo.png similarity index 100% rename from apps/campaign/src/assets/discord-logo.png rename to apps/app/src/assets/discord-logo.png diff --git a/apps/campaign/src/assets/gitcoin-logo.svg b/apps/app/src/assets/gitcoin-logo.svg similarity index 100% rename from apps/campaign/src/assets/gitcoin-logo.svg rename to apps/app/src/assets/gitcoin-logo.svg diff --git a/apps/campaign/src/assets/github-logo.png b/apps/app/src/assets/github-logo.png similarity index 100% rename from apps/campaign/src/assets/github-logo.png rename to apps/app/src/assets/github-logo.png diff --git a/apps/campaign/src/assets/google-logo.png b/apps/app/src/assets/google-logo.png similarity index 100% rename from apps/campaign/src/assets/google-logo.png rename to apps/app/src/assets/google-logo.png diff --git a/apps/campaign/src/assets/logo-eu.png b/apps/app/src/assets/logo-eu.png similarity index 100% rename from apps/campaign/src/assets/logo-eu.png rename to apps/app/src/assets/logo-eu.png diff --git a/apps/campaign/src/assets/logo-pv.png b/apps/app/src/assets/logo-pv.png similarity index 100% rename from apps/campaign/src/assets/logo-pv.png rename to apps/app/src/assets/logo-pv.png diff --git a/apps/campaign/src/assets/logo-ts.png b/apps/app/src/assets/logo-ts.png similarity index 100% rename from apps/campaign/src/assets/logo-ts.png rename to apps/app/src/assets/logo-ts.png diff --git a/apps/app/src/assets/logo.png b/apps/app/src/assets/logo.png new file mode 100644 index 000000000..789f97fdc Binary files /dev/null and b/apps/app/src/assets/logo.png differ diff --git a/apps/campaign/src/assets/metamask-logo.png b/apps/app/src/assets/metamask-logo.png similarity index 100% rename from apps/campaign/src/assets/metamask-logo.png rename to apps/app/src/assets/metamask-logo.png diff --git a/apps/campaign/src/assets/safe-logo.jpg b/apps/app/src/assets/safe-logo.jpg similarity index 100% rename from apps/campaign/src/assets/safe-logo.jpg rename to apps/app/src/assets/safe-logo.jpg diff --git a/apps/campaign/src/assets/shopify-logo.png b/apps/app/src/assets/shopify-logo.png similarity index 100% rename from apps/campaign/src/assets/shopify-logo.png rename to apps/app/src/assets/shopify-logo.png diff --git a/apps/campaign/src/assets/thx_logo_arbitrum.svg b/apps/app/src/assets/thx_logo_arbitrum.svg similarity index 100% rename from apps/campaign/src/assets/thx_logo_arbitrum.svg rename to apps/app/src/assets/thx_logo_arbitrum.svg diff --git a/apps/campaign/src/assets/thx_logo_bsc.svg b/apps/app/src/assets/thx_logo_bsc.svg similarity index 100% rename from apps/campaign/src/assets/thx_logo_bsc.svg rename to apps/app/src/assets/thx_logo_bsc.svg diff --git a/apps/campaign/src/assets/thx_logo_ethereum.svg b/apps/app/src/assets/thx_logo_ethereum.svg similarity index 100% rename from apps/campaign/src/assets/thx_logo_ethereum.svg rename to apps/app/src/assets/thx_logo_ethereum.svg diff --git a/apps/campaign/src/assets/thx_logo_hardhat.svg b/apps/app/src/assets/thx_logo_hardhat.svg similarity index 100% rename from apps/campaign/src/assets/thx_logo_hardhat.svg rename to apps/app/src/assets/thx_logo_hardhat.svg diff --git a/apps/campaign/src/assets/thx_logo_linea.svg b/apps/app/src/assets/thx_logo_linea.svg similarity index 100% rename from apps/campaign/src/assets/thx_logo_linea.svg rename to apps/app/src/assets/thx_logo_linea.svg diff --git a/apps/campaign/src/assets/thx_logo_polygon.svg b/apps/app/src/assets/thx_logo_polygon.svg similarity index 100% rename from apps/campaign/src/assets/thx_logo_polygon.svg rename to apps/app/src/assets/thx_logo_polygon.svg diff --git a/apps/campaign/src/assets/thx_token_governance.png b/apps/app/src/assets/thx_token_governance.png similarity index 100% rename from apps/campaign/src/assets/thx_token_governance.png rename to apps/app/src/assets/thx_token_governance.png diff --git a/apps/campaign/src/assets/thx_token_subscribe.webp b/apps/app/src/assets/thx_token_subscribe.webp similarity index 100% rename from apps/campaign/src/assets/thx_token_subscribe.webp rename to apps/app/src/assets/thx_token_subscribe.webp diff --git a/apps/campaign/src/assets/twitter-logo.png b/apps/app/src/assets/twitter-logo.png similarity index 100% rename from apps/campaign/src/assets/twitter-logo.png rename to apps/app/src/assets/twitter-logo.png diff --git a/apps/campaign/src/assets/walletconnect-logo.png b/apps/app/src/assets/walletconnect-logo.png similarity index 100% rename from apps/campaign/src/assets/walletconnect-logo.png rename to apps/app/src/assets/walletconnect-logo.png diff --git a/apps/campaign/src/assets/web3auth-logo.jpeg b/apps/app/src/assets/web3auth-logo.jpeg similarity index 100% rename from apps/campaign/src/assets/web3auth-logo.jpeg rename to apps/app/src/assets/web3auth-logo.jpeg diff --git a/apps/campaign/src/assets/youtube-logo.png b/apps/app/src/assets/youtube-logo.png similarity index 100% rename from apps/campaign/src/assets/youtube-logo.png rename to apps/app/src/assets/youtube-logo.png diff --git a/apps/campaign/src/components/BaseFooter.vue b/apps/app/src/components/BaseFooter.vue similarity index 100% rename from apps/campaign/src/components/BaseFooter.vue rename to apps/app/src/components/BaseFooter.vue diff --git a/apps/campaign/src/components/BaseQuestLeaderboard.vue b/apps/app/src/components/BaseQuestLeaderboard.vue similarity index 100% rename from apps/campaign/src/components/BaseQuestLeaderboard.vue rename to apps/app/src/components/BaseQuestLeaderboard.vue diff --git a/apps/campaign/src/components/BaseSidebar.vue b/apps/app/src/components/BaseSidebar.vue similarity index 100% rename from apps/campaign/src/components/BaseSidebar.vue rename to apps/app/src/components/BaseSidebar.vue diff --git a/apps/campaign/src/components/alert/BaseAlertErrorList.vue b/apps/app/src/components/alert/BaseAlertErrorList.vue similarity index 100% rename from apps/campaign/src/components/alert/BaseAlertErrorList.vue rename to apps/app/src/components/alert/BaseAlertErrorList.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteDiscordInviteUsed.vue b/apps/app/src/components/blockquote/BaseBlockquoteDiscordInviteUsed.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteDiscordInviteUsed.vue rename to apps/app/src/components/blockquote/BaseBlockquoteDiscordInviteUsed.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteDiscordMessage.vue b/apps/app/src/components/blockquote/BaseBlockquoteDiscordMessage.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteDiscordMessage.vue rename to apps/app/src/components/blockquote/BaseBlockquoteDiscordMessage.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteDiscordServerJoin.vue b/apps/app/src/components/blockquote/BaseBlockquoteDiscordServerJoin.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteDiscordServerJoin.vue rename to apps/app/src/components/blockquote/BaseBlockquoteDiscordServerJoin.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteDiscordServerRole.vue b/apps/app/src/components/blockquote/BaseBlockquoteDiscordServerRole.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteDiscordServerRole.vue rename to apps/app/src/components/blockquote/BaseBlockquoteDiscordServerRole.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteTwitterQuery.vue b/apps/app/src/components/blockquote/BaseBlockquoteTwitterQuery.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteTwitterQuery.vue rename to apps/app/src/components/blockquote/BaseBlockquoteTwitterQuery.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteTwitterTweet.vue b/apps/app/src/components/blockquote/BaseBlockquoteTwitterTweet.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteTwitterTweet.vue rename to apps/app/src/components/blockquote/BaseBlockquoteTwitterTweet.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteTwitterUser.vue b/apps/app/src/components/blockquote/BaseBlockquoteTwitterUser.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteTwitterUser.vue rename to apps/app/src/components/blockquote/BaseBlockquoteTwitterUser.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteVideo.vue b/apps/app/src/components/blockquote/BaseBlockquoteVideo.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteVideo.vue rename to apps/app/src/components/blockquote/BaseBlockquoteVideo.vue diff --git a/apps/campaign/src/components/blockquote/BaseBlockquoteYoutubeChannelSubscription.vue b/apps/app/src/components/blockquote/BaseBlockquoteYoutubeChannelSubscription.vue similarity index 100% rename from apps/campaign/src/components/blockquote/BaseBlockquoteYoutubeChannelSubscription.vue rename to apps/app/src/components/blockquote/BaseBlockquoteYoutubeChannelSubscription.vue diff --git a/apps/campaign/src/components/button/BaseBtnShareEmail.vue b/apps/app/src/components/button/BaseBtnShareEmail.vue similarity index 100% rename from apps/campaign/src/components/button/BaseBtnShareEmail.vue rename to apps/app/src/components/button/BaseBtnShareEmail.vue diff --git a/apps/campaign/src/components/button/BaseBtnShareLinkedin.vue b/apps/app/src/components/button/BaseBtnShareLinkedin.vue similarity index 100% rename from apps/campaign/src/components/button/BaseBtnShareLinkedin.vue rename to apps/app/src/components/button/BaseBtnShareLinkedin.vue diff --git a/apps/campaign/src/components/button/BaseBtnShareTelegram.vue b/apps/app/src/components/button/BaseBtnShareTelegram.vue similarity index 100% rename from apps/campaign/src/components/button/BaseBtnShareTelegram.vue rename to apps/app/src/components/button/BaseBtnShareTelegram.vue diff --git a/apps/campaign/src/components/button/BaseBtnShareTwitter.vue b/apps/app/src/components/button/BaseBtnShareTwitter.vue similarity index 100% rename from apps/campaign/src/components/button/BaseBtnShareTwitter.vue rename to apps/app/src/components/button/BaseBtnShareTwitter.vue diff --git a/apps/campaign/src/components/button/BaseBtnShareWhatsapp.vue b/apps/app/src/components/button/BaseBtnShareWhatsapp.vue similarity index 100% rename from apps/campaign/src/components/button/BaseBtnShareWhatsapp.vue rename to apps/app/src/components/button/BaseBtnShareWhatsapp.vue diff --git a/apps/campaign/src/components/button/BaseButtonApprove.vue b/apps/app/src/components/button/BaseButtonApprove.vue similarity index 94% rename from apps/campaign/src/components/button/BaseButtonApprove.vue rename to apps/app/src/components/button/BaseButtonApprove.vue index 22237a296..d7219f170 100644 --- a/apps/campaign/src/components/button/BaseButtonApprove.vue +++ b/apps/app/src/components/button/BaseButtonApprove.vue @@ -9,10 +9,10 @@ import { defineComponent, PropType } from 'vue'; import { BigNumber } from 'ethers/lib/ethers'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; import { mapStores } from 'pinia'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { ChainId } from '@thxnetwork/sdk'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { ChainId } from '@thxnetwork/common/enums'; import poll from 'promise-poller'; export default defineComponent({ diff --git a/apps/campaign/src/components/button/BaseButtonLiquidityCreate.vue b/apps/app/src/components/button/BaseButtonLiquidityCreate.vue similarity index 90% rename from apps/campaign/src/components/button/BaseButtonLiquidityCreate.vue rename to apps/app/src/components/button/BaseButtonLiquidityCreate.vue index b361c5d30..e42e0cd97 100644 --- a/apps/campaign/src/components/button/BaseButtonLiquidityCreate.vue +++ b/apps/app/src/components/button/BaseButtonLiquidityCreate.vue @@ -8,13 +8,13 @@ <script lang="ts"> import { defineComponent, PropType } from 'vue'; import { BigNumber } from 'ethers/lib/ethers'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; import { mapStores } from 'pinia'; -import { BALANCER_POOL_ID, contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { ChainId } from '@thxnetwork/sdk'; +import { BALANCER_POOL_ID, contractNetworks } from '@thxnetwork/app/config/constants'; +import { ChainId } from '@thxnetwork/common/enums'; import { BalancerSDK, Network } from '@balancer-labs/sdk'; -import { POLYGON_RPC } from '@thxnetwork/campaign/config/secrets'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; +import { POLYGON_RPC } from '@thxnetwork/app/config/secrets'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; export default defineComponent({ name: 'BaseButtonLiquidityCreate', diff --git a/apps/campaign/src/components/button/BaseButtonLiquidityLock.vue b/apps/app/src/components/button/BaseButtonLiquidityLock.vue similarity index 92% rename from apps/campaign/src/components/button/BaseButtonLiquidityLock.vue rename to apps/app/src/components/button/BaseButtonLiquidityLock.vue index 08f42ed2a..624fd5cc8 100644 --- a/apps/campaign/src/components/button/BaseButtonLiquidityLock.vue +++ b/apps/app/src/components/button/BaseButtonLiquidityLock.vue @@ -8,9 +8,9 @@ <script lang="ts"> import { defineComponent, PropType } from 'vue'; import { BigNumber } from 'ethers/lib/ethers'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; import { mapStores } from 'pinia'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; export default defineComponent({ name: 'BaseButtonLiquidityLock', diff --git a/apps/campaign/src/components/button/BaseButtonLiquidityStake.vue b/apps/app/src/components/button/BaseButtonLiquidityStake.vue similarity index 87% rename from apps/campaign/src/components/button/BaseButtonLiquidityStake.vue rename to apps/app/src/components/button/BaseButtonLiquidityStake.vue index 2ef44dc29..ad416942e 100644 --- a/apps/campaign/src/components/button/BaseButtonLiquidityStake.vue +++ b/apps/app/src/components/button/BaseButtonLiquidityStake.vue @@ -8,11 +8,11 @@ <script lang="ts"> import { defineComponent, PropType } from 'vue'; import { BigNumber } from 'ethers/lib/ethers'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; import { mapStores } from 'pinia'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { ChainId } from '@thxnetwork/sdk'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { ChainId } from '@thxnetwork/common/enums'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; export default defineComponent({ name: 'BaseButtonLiquidityStake', diff --git a/apps/campaign/src/components/button/BaseButtonQuestLocked.vue b/apps/app/src/components/button/BaseButtonQuestLocked.vue similarity index 100% rename from apps/campaign/src/components/button/BaseButtonQuestLocked.vue rename to apps/app/src/components/button/BaseButtonQuestLocked.vue diff --git a/apps/campaign/src/components/button/BaseButtonWalletConnect.vue b/apps/app/src/components/button/BaseButtonWalletConnect.vue similarity index 100% rename from apps/campaign/src/components/button/BaseButtonWalletConnect.vue rename to apps/app/src/components/button/BaseButtonWalletConnect.vue diff --git a/apps/campaign/src/components/card/BaseCardAccount.vue b/apps/app/src/components/card/BaseCardAccount.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardAccount.vue rename to apps/app/src/components/card/BaseCardAccount.vue diff --git a/apps/campaign/src/components/card/BaseCardAccountRank.vue b/apps/app/src/components/card/BaseCardAccountRank.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardAccountRank.vue rename to apps/app/src/components/card/BaseCardAccountRank.vue diff --git a/apps/campaign/src/components/card/BaseCardCampaign.vue b/apps/app/src/components/card/BaseCardCampaign.vue similarity index 98% rename from apps/campaign/src/components/card/BaseCardCampaign.vue rename to apps/app/src/components/card/BaseCardCampaign.vue index 4d88ff507..0e78456e9 100644 --- a/apps/campaign/src/components/card/BaseCardCampaign.vue +++ b/apps/app/src/components/card/BaseCardCampaign.vue @@ -91,7 +91,7 @@ import { defineComponent, PropType } from 'vue'; import { decodeHTML } from '../../utils/decode-html'; import { mapStores } from 'pinia'; -import { useAccountStore } from '@thxnetwork/campaign/stores/Account'; +import { useAccountStore } from '@thxnetwork/app/stores/Account'; type TCampaignProps = { id: string; diff --git a/apps/campaign/src/components/card/BaseCardCouponCode.vue b/apps/app/src/components/card/BaseCardCouponCode.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardCouponCode.vue rename to apps/app/src/components/card/BaseCardCouponCode.vue diff --git a/apps/campaign/src/components/card/BaseCardDiscordRole.vue b/apps/app/src/components/card/BaseCardDiscordRole.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardDiscordRole.vue rename to apps/app/src/components/card/BaseCardDiscordRole.vue diff --git a/apps/campaign/src/components/card/BaseCardERC20.vue b/apps/app/src/components/card/BaseCardERC20.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardERC20.vue rename to apps/app/src/components/card/BaseCardERC20.vue diff --git a/apps/campaign/src/components/card/BaseCardERC721.vue b/apps/app/src/components/card/BaseCardERC721.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardERC721.vue rename to apps/app/src/components/card/BaseCardERC721.vue diff --git a/apps/campaign/src/components/card/BaseCardGalachain.vue b/apps/app/src/components/card/BaseCardGalachain.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardGalachain.vue rename to apps/app/src/components/card/BaseCardGalachain.vue diff --git a/apps/campaign/src/components/card/BaseCardHeader.vue b/apps/app/src/components/card/BaseCardHeader.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardHeader.vue rename to apps/app/src/components/card/BaseCardHeader.vue diff --git a/apps/campaign/src/components/card/BaseCardHeaderHome.vue b/apps/app/src/components/card/BaseCardHeaderHome.vue similarity index 87% rename from apps/campaign/src/components/card/BaseCardHeaderHome.vue rename to apps/app/src/components/card/BaseCardHeaderHome.vue index bc63925ca..d2a187541 100644 --- a/apps/campaign/src/components/card/BaseCardHeaderHome.vue +++ b/apps/app/src/components/card/BaseCardHeaderHome.vue @@ -39,12 +39,12 @@ </template> <script lang="ts"> -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; -import imgLogoEU from '@thxnetwork/campaign/assets/logo-eu.png'; -import imgLogoTS from '@thxnetwork/campaign/assets/logo-ts.png'; -import imgLogoPV from '@thxnetwork/campaign/assets/logo-pv.png'; +import imgLogoEU from '@thxnetwork/app/assets/logo-eu.png'; +import imgLogoTS from '@thxnetwork/app/assets/logo-ts.png'; +import imgLogoPV from '@thxnetwork/app/assets/logo-pv.png'; export default defineComponent({ name: 'BaseCardHeaderHome', diff --git a/apps/app/src/components/card/BaseCardMembership.vue b/apps/app/src/components/card/BaseCardMembership.vue new file mode 100644 index 000000000..bb193615e --- /dev/null +++ b/apps/app/src/components/card/BaseCardMembership.vue @@ -0,0 +1,56 @@ +<template> + <b-card class="border-0 gradient-shadow-xl" style="min-height: 415px"> + <b-tabs v-model="index" pills justified content-class="mt-3" nav-wrapper-class="text-white"> + <b-tab> + <template #title> + <i class="fas fa-balance-scale me-1" /> + Liquidity + </template> + <hr /> + <BaseTabLiquidity @change-tab="index = $event" /> + </b-tab> + <b-tab> + <template #title> + <i class="fas fa-id-card me-1" /> + Membership + </template> + <hr /> + <BaseTabDeposit v-if="veStore.lock && !Number(veStore.lock.amount)" @change-tab="index = $event" /> + <BaseTabWithdraw v-else @change-tab="index = $event" /> + </b-tab> + </b-tabs> + </b-card> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import { mapStores } from 'pinia'; +import { useVeStore } from '../../stores/VE'; + +export default defineComponent({ + name: 'BaseCardMembership', + props: { + tabIndex: { + type: Number, + default: 0, + }, + }, + data() { + return { + index: 0, + }; + }, + computed: { + ...mapStores(useVeStore), + }, + watch: { + tabIndex: { + handler(value: number) { + this.index = value; + }, + immediate: true, + }, + }, + methods: {}, +}); +</script> diff --git a/apps/campaign/src/components/card/BaseCardMembershipOnboarding.vue b/apps/app/src/components/card/BaseCardMembershipOnboarding.vue similarity index 96% rename from apps/campaign/src/components/card/BaseCardMembershipOnboarding.vue rename to apps/app/src/components/card/BaseCardMembershipOnboarding.vue index 1fbc53d72..9c12ad186 100644 --- a/apps/campaign/src/components/card/BaseCardMembershipOnboarding.vue +++ b/apps/app/src/components/card/BaseCardMembershipOnboarding.vue @@ -88,11 +88,11 @@ import { mapStores } from 'pinia'; import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { useVeStore } from '../../stores/VE'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; -import { ChainId } from '@thxnetwork/sdk'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { chainList } from '@thxnetwork/app/utils/chains'; +import { ChainId } from '@thxnetwork/common/enums'; import { formatUnits } from 'ethers/lib/utils'; -import { useAuthStore } from '@thxnetwork/campaign/stores/Auth'; +import { useAuthStore } from '@thxnetwork/app/stores/Auth'; import { useAccountStore } from '../../stores/Account'; export default defineComponent({ diff --git a/apps/campaign/src/components/card/BaseCardPrices.vue b/apps/app/src/components/card/BaseCardPrices.vue similarity index 96% rename from apps/campaign/src/components/card/BaseCardPrices.vue rename to apps/app/src/components/card/BaseCardPrices.vue index 525fee560..f41b3d86a 100644 --- a/apps/campaign/src/components/card/BaseCardPrices.vue +++ b/apps/app/src/components/card/BaseCardPrices.vue @@ -23,7 +23,7 @@ import { useAuthStore } from '../../stores/Auth'; import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { useVeStore } from '../../stores/VE'; -import { roundUpFixed, toFiatPrice } from '@thxnetwork/campaign/utils/price'; +import { roundUpFixed, toFiatPrice } from '@thxnetwork/app/utils/price'; export default defineComponent({ name: 'BaseCardPrices', diff --git a/apps/campaign/src/components/card/BaseCardQuest.vue b/apps/app/src/components/card/BaseCardQuest.vue similarity index 98% rename from apps/campaign/src/components/card/BaseCardQuest.vue rename to apps/app/src/components/card/BaseCardQuest.vue index 526f05dfa..25def26c9 100644 --- a/apps/campaign/src/components/card/BaseCardQuest.vue +++ b/apps/app/src/components/card/BaseCardQuest.vue @@ -117,11 +117,11 @@ <script lang="ts"> import { PropType, defineComponent } from 'vue'; import { format, formatDistance } from 'date-fns'; -import { QuestVariant } from '@thxnetwork/sdk'; import { mapStores } from 'pinia'; import { useAccountStore } from '../../stores/Account'; import { useQuestStore } from '../../stores/Quest'; -import { decodeHTML } from '@thxnetwork/campaign/utils/decode-html'; +import { decodeHTML } from '@thxnetwork/app/utils/decode-html'; +import { QuestVariant } from '@thxnetwork/sdk/types/enums'; export default defineComponent({ name: 'BaseCardQuest', diff --git a/apps/campaign/src/components/card/BaseCardQuestCustom.vue b/apps/app/src/components/card/BaseCardQuestCustom.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardQuestCustom.vue rename to apps/app/src/components/card/BaseCardQuestCustom.vue diff --git a/apps/campaign/src/components/card/BaseCardQuestDaily.vue b/apps/app/src/components/card/BaseCardQuestDaily.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardQuestDaily.vue rename to apps/app/src/components/card/BaseCardQuestDaily.vue diff --git a/apps/campaign/src/components/card/BaseCardQuestGitcoin.vue b/apps/app/src/components/card/BaseCardQuestGitcoin.vue similarity index 96% rename from apps/campaign/src/components/card/BaseCardQuestGitcoin.vue rename to apps/app/src/components/card/BaseCardQuestGitcoin.vue index bf372e06f..f9bf004cd 100644 --- a/apps/campaign/src/components/card/BaseCardQuestGitcoin.vue +++ b/apps/app/src/components/card/BaseCardQuestGitcoin.vue @@ -57,8 +57,8 @@ import { useAccountStore } from '../../stores/Account'; import { useAuthStore } from '../../stores/Auth'; import { useQuestStore } from '../../stores/Quest'; import { useWalletStore } from '../../stores/Wallet'; -import { ChainId } from '@thxnetwork/sdk'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { WalletVariant } from '../../types/enums/accountVariant'; +import { ChainId } from '@thxnetwork/common/enums'; import imgLogoGitcoin from '../../assets/gitcoin-logo.svg'; export default defineComponent({ diff --git a/apps/campaign/src/components/card/BaseCardQuestInvite.vue b/apps/app/src/components/card/BaseCardQuestInvite.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardQuestInvite.vue rename to apps/app/src/components/card/BaseCardQuestInvite.vue diff --git a/apps/campaign/src/components/card/BaseCardQuestSocial.vue b/apps/app/src/components/card/BaseCardQuestSocial.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardQuestSocial.vue rename to apps/app/src/components/card/BaseCardQuestSocial.vue diff --git a/apps/campaign/src/components/card/BaseCardQuestSpotlight.vue b/apps/app/src/components/card/BaseCardQuestSpotlight.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardQuestSpotlight.vue rename to apps/app/src/components/card/BaseCardQuestSpotlight.vue diff --git a/apps/campaign/src/components/card/BaseCardQuestWeb3.vue b/apps/app/src/components/card/BaseCardQuestWeb3.vue similarity index 95% rename from apps/campaign/src/components/card/BaseCardQuestWeb3.vue rename to apps/app/src/components/card/BaseCardQuestWeb3.vue index f3f7e3412..46644a481 100644 --- a/apps/campaign/src/components/card/BaseCardQuestWeb3.vue +++ b/apps/app/src/components/card/BaseCardQuestWeb3.vue @@ -66,9 +66,9 @@ import { useAccountStore } from '../../stores/Account'; import { useAuthStore } from '../../stores/Auth'; import { useQuestStore } from '../../stores/Quest'; import { chainList, getAddressURL } from '../../utils/chains'; -import { ChainId } from '@thxnetwork/sdk/src/lib/types/enums/ChainId'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { ChainId } from '@thxnetwork/common/enums'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; export default defineComponent({ name: 'BaseCardQuestWeb3', diff --git a/apps/campaign/src/components/card/BaseCardQuestWebhook.vue b/apps/app/src/components/card/BaseCardQuestWebhook.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardQuestWebhook.vue rename to apps/app/src/components/card/BaseCardQuestWebhook.vue diff --git a/apps/campaign/src/components/card/BaseCardReward.vue b/apps/app/src/components/card/BaseCardReward.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardReward.vue rename to apps/app/src/components/card/BaseCardReward.vue diff --git a/apps/campaign/src/components/card/BaseCardRewardCoin.vue b/apps/app/src/components/card/BaseCardRewardCoin.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewardCoin.vue rename to apps/app/src/components/card/BaseCardRewardCoin.vue diff --git a/apps/campaign/src/components/card/BaseCardRewardCoupon.vue b/apps/app/src/components/card/BaseCardRewardCoupon.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewardCoupon.vue rename to apps/app/src/components/card/BaseCardRewardCoupon.vue diff --git a/apps/campaign/src/components/card/BaseCardRewardCustom.vue b/apps/app/src/components/card/BaseCardRewardCustom.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewardCustom.vue rename to apps/app/src/components/card/BaseCardRewardCustom.vue diff --git a/apps/campaign/src/components/card/BaseCardRewardDiscordRole.vue b/apps/app/src/components/card/BaseCardRewardDiscordRole.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewardDiscordRole.vue rename to apps/app/src/components/card/BaseCardRewardDiscordRole.vue diff --git a/apps/campaign/src/components/card/BaseCardRewardGalachain.vue b/apps/app/src/components/card/BaseCardRewardGalachain.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewardGalachain.vue rename to apps/app/src/components/card/BaseCardRewardGalachain.vue diff --git a/apps/campaign/src/components/card/BaseCardRewardNFT.vue b/apps/app/src/components/card/BaseCardRewardNFT.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewardNFT.vue rename to apps/app/src/components/card/BaseCardRewardNFT.vue diff --git a/apps/campaign/src/components/card/BaseCardRewards.vue b/apps/app/src/components/card/BaseCardRewards.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardRewards.vue rename to apps/app/src/components/card/BaseCardRewards.vue diff --git a/apps/campaign/src/components/card/BaseCardSnapshotProposal.vue b/apps/app/src/components/card/BaseCardSnapshotProposal.vue similarity index 98% rename from apps/campaign/src/components/card/BaseCardSnapshotProposal.vue rename to apps/app/src/components/card/BaseCardSnapshotProposal.vue index bc7b16ff5..a4e0af0c3 100644 --- a/apps/campaign/src/components/card/BaseCardSnapshotProposal.vue +++ b/apps/app/src/components/card/BaseCardSnapshotProposal.vue @@ -64,7 +64,7 @@ import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; import { useSnapshotStore } from '../../stores/Snapshot'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; import { format } from 'date-fns'; export default defineComponent({ diff --git a/apps/campaign/src/components/card/BaseCardStatusCheck.vue b/apps/app/src/components/card/BaseCardStatusCheck.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardStatusCheck.vue rename to apps/app/src/components/card/BaseCardStatusCheck.vue diff --git a/apps/campaign/src/components/card/BaseCardWallets.vue b/apps/app/src/components/card/BaseCardWallets.vue similarity index 100% rename from apps/campaign/src/components/card/BaseCardWallets.vue rename to apps/app/src/components/card/BaseCardWallets.vue diff --git a/apps/campaign/src/components/dropdown/BaseDropdownMetric.vue b/apps/app/src/components/dropdown/BaseDropdownMetric.vue similarity index 100% rename from apps/campaign/src/components/dropdown/BaseDropdownMetric.vue rename to apps/app/src/components/dropdown/BaseDropdownMetric.vue diff --git a/apps/campaign/src/components/dropdown/BaseDropdownMetricAPR.vue b/apps/app/src/components/dropdown/BaseDropdownMetricAPR.vue similarity index 100% rename from apps/campaign/src/components/dropdown/BaseDropdownMetricAPR.vue rename to apps/app/src/components/dropdown/BaseDropdownMetricAPR.vue diff --git a/apps/campaign/src/components/dropdown/BaseDropdownMetricReward.vue b/apps/app/src/components/dropdown/BaseDropdownMetricReward.vue similarity index 89% rename from apps/campaign/src/components/dropdown/BaseDropdownMetricReward.vue rename to apps/app/src/components/dropdown/BaseDropdownMetricReward.vue index ef1bccff6..ab04a3d46 100644 --- a/apps/campaign/src/components/dropdown/BaseDropdownMetricReward.vue +++ b/apps/app/src/components/dropdown/BaseDropdownMetricReward.vue @@ -31,10 +31,10 @@ import { defineComponent, PropType } from 'vue'; import { useLiquidityStore } from '../../stores/Liquidity'; import { useWalletStore } from '../../stores/Wallet'; import { formatUnits } from 'ethers/lib/utils'; -import { toFiatPrice } from '@thxnetwork/campaign/utils/price'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { ChainId } from '@thxnetwork/sdk'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; +import { toFiatPrice } from '@thxnetwork/app/utils/price'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { ChainId } from '@thxnetwork/common/enums'; +import { chainList } from '@thxnetwork/app/utils/chains'; type TMetricReward = { week: string; diff --git a/apps/campaign/src/components/dropdown/BaseDropdownMetricRewards.vue b/apps/app/src/components/dropdown/BaseDropdownMetricRewards.vue similarity index 89% rename from apps/campaign/src/components/dropdown/BaseDropdownMetricRewards.vue rename to apps/app/src/components/dropdown/BaseDropdownMetricRewards.vue index c40266ff8..c4fd38a26 100644 --- a/apps/campaign/src/components/dropdown/BaseDropdownMetricRewards.vue +++ b/apps/app/src/components/dropdown/BaseDropdownMetricRewards.vue @@ -31,12 +31,12 @@ import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; import { useLiquidityStore } from '../../stores/Liquidity'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { toFiatPrice } from '@thxnetwork/campaign/utils/price'; +import { toFiatPrice } from '@thxnetwork/app/utils/price'; import { BigNumber } from 'ethers/lib/ethers'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; -import { ChainId } from '@thxnetwork/sdk'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; +import { chainList } from '@thxnetwork/app/utils/chains'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; +import { ChainId } from '@thxnetwork/common/enums'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; export default defineComponent({ name: 'BaseDropdownMetricAPR', diff --git a/apps/campaign/src/components/dropdown/BaseDropdownMetricTVL.vue b/apps/app/src/components/dropdown/BaseDropdownMetricTVL.vue similarity index 92% rename from apps/campaign/src/components/dropdown/BaseDropdownMetricTVL.vue rename to apps/app/src/components/dropdown/BaseDropdownMetricTVL.vue index 963341551..a1e4b4d30 100644 --- a/apps/campaign/src/components/dropdown/BaseDropdownMetricTVL.vue +++ b/apps/app/src/components/dropdown/BaseDropdownMetricTVL.vue @@ -40,10 +40,10 @@ import { defineComponent } from 'vue'; import { useLiquidityStore } from '../../stores/Liquidity'; import { useWalletStore } from '../../stores/Wallet'; import { formatUnits } from 'ethers/lib/utils'; -import { toFiatPrice } from '@thxnetwork/campaign/utils/price'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { ChainId } from '@thxnetwork/sdk'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; +import { toFiatPrice } from '@thxnetwork/app/utils/price'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { ChainId } from '@thxnetwork/common/enums'; +import { chainList } from '@thxnetwork/app/utils/chains'; export default defineComponent({ name: 'BaseDropdownMetricAPR', diff --git a/apps/campaign/src/components/dropdown/BaseDropdownUserMenu.vue b/apps/app/src/components/dropdown/BaseDropdownUserMenu.vue similarity index 100% rename from apps/campaign/src/components/dropdown/BaseDropdownUserMenu.vue rename to apps/app/src/components/dropdown/BaseDropdownUserMenu.vue diff --git a/apps/campaign/src/components/dropdown/BaseDropdownWallets.vue b/apps/app/src/components/dropdown/BaseDropdownWallets.vue similarity index 99% rename from apps/campaign/src/components/dropdown/BaseDropdownWallets.vue rename to apps/app/src/components/dropdown/BaseDropdownWallets.vue index ea5f82f86..40ef5b77a 100644 --- a/apps/campaign/src/components/dropdown/BaseDropdownWallets.vue +++ b/apps/app/src/components/dropdown/BaseDropdownWallets.vue @@ -153,7 +153,7 @@ import { mapStores } from 'pinia'; import { useWalletStore, walletLogoMap } from '../../stores/Wallet'; import { useAccountStore } from '../../stores/Account'; import { WalletVariant } from '../../types/enums/accountVariant'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; +import { chainList } from '@thxnetwork/app/utils/chains'; export default defineComponent({ name: 'BaseDropdownWallets', diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupAccountVariant.vue b/apps/app/src/components/formgroup/BaseFormGroupAccountVariant.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupAccountVariant.vue rename to apps/app/src/components/formgroup/BaseFormGroupAccountVariant.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupAvatar.vue b/apps/app/src/components/formgroup/BaseFormGroupAvatar.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupAvatar.vue rename to apps/app/src/components/formgroup/BaseFormGroupAvatar.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupConnected.vue b/apps/app/src/components/formgroup/BaseFormGroupConnected.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupConnected.vue rename to apps/app/src/components/formgroup/BaseFormGroupConnected.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupEmail.vue b/apps/app/src/components/formgroup/BaseFormGroupEmail.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupEmail.vue rename to apps/app/src/components/formgroup/BaseFormGroupEmail.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupInputDate.vue b/apps/app/src/components/formgroup/BaseFormGroupInputDate.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupInputDate.vue rename to apps/app/src/components/formgroup/BaseFormGroupInputDate.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupInputTokenAmount.vue b/apps/app/src/components/formgroup/BaseFormGroupInputTokenAmount.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupInputTokenAmount.vue rename to apps/app/src/components/formgroup/BaseFormGroupInputTokenAmount.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupLockAmount.vue b/apps/app/src/components/formgroup/BaseFormGroupLockAmount.vue similarity index 88% rename from apps/campaign/src/components/formgroup/BaseFormGroupLockAmount.vue rename to apps/app/src/components/formgroup/BaseFormGroupLockAmount.vue index b3186681f..a96a0f58a 100644 --- a/apps/campaign/src/components/formgroup/BaseFormGroupLockAmount.vue +++ b/apps/app/src/components/formgroup/BaseFormGroupLockAmount.vue @@ -32,11 +32,11 @@ </template> <script lang="ts"> -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; -import { ChainId } from '@thxnetwork/sdk'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; +import { ChainId } from '@thxnetwork/common/enums'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupLockEnd.vue b/apps/app/src/components/formgroup/BaseFormGroupLockEnd.vue similarity index 95% rename from apps/campaign/src/components/formgroup/BaseFormGroupLockEnd.vue rename to apps/app/src/components/formgroup/BaseFormGroupLockEnd.vue index 4a5626620..1a282215a 100644 --- a/apps/campaign/src/components/formgroup/BaseFormGroupLockEnd.vue +++ b/apps/app/src/components/formgroup/BaseFormGroupLockEnd.vue @@ -29,10 +29,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { NinetyDaysInMs, getThursdaysUntilTimestamp } from '@thxnetwork/campaign/utils/date'; +import { NinetyDaysInMs, getThursdaysUntilTimestamp } from '@thxnetwork/app/utils/date'; import { mapStores } from 'pinia'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; -import { BigNumber } from 'ethers'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; +import { BigNumber } from 'alchemy-sdk'; export default defineComponent({ name: 'BaseFormGroupLockEnd', diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupUsername.vue b/apps/app/src/components/formgroup/BaseFormGroupUsername.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupUsername.vue rename to apps/app/src/components/formgroup/BaseFormGroupUsername.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupWalletAddress.vue b/apps/app/src/components/formgroup/BaseFormGroupWalletAddress.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupWalletAddress.vue rename to apps/app/src/components/formgroup/BaseFormGroupWalletAddress.vue diff --git a/apps/campaign/src/components/formgroup/BaseFormGroupWalletSelect.vue b/apps/app/src/components/formgroup/BaseFormGroupWalletSelect.vue similarity index 100% rename from apps/campaign/src/components/formgroup/BaseFormGroupWalletSelect.vue rename to apps/app/src/components/formgroup/BaseFormGroupWalletSelect.vue diff --git a/apps/campaign/src/components/modal/BaseModalAccount.vue b/apps/app/src/components/modal/BaseModalAccount.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalAccount.vue rename to apps/app/src/components/modal/BaseModalAccount.vue diff --git a/apps/campaign/src/components/modal/BaseModalClaimTokens.vue b/apps/app/src/components/modal/BaseModalClaimTokens.vue similarity index 93% rename from apps/campaign/src/components/modal/BaseModalClaimTokens.vue rename to apps/app/src/components/modal/BaseModalClaimTokens.vue index 0528c9b61..19af3f7b1 100644 --- a/apps/campaign/src/components/modal/BaseModalClaimTokens.vue +++ b/apps/app/src/components/modal/BaseModalClaimTokens.vue @@ -70,11 +70,11 @@ import { mapStores } from 'pinia'; import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; import { formatUnits } from 'ethers/lib/utils'; -import { roundDownFixed, toFiatPrice } from '@thxnetwork/campaign/utils/price'; -import { ChainId } from '@thxnetwork/sdk'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { roundDownFixed, toFiatPrice } from '@thxnetwork/app/utils/price'; +import { ChainId } from '@thxnetwork/common/enums'; +import { chainList } from '@thxnetwork/app/utils/chains'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; export default defineComponent({ name: 'BaseModalClaimTokens', diff --git a/apps/campaign/src/components/modal/BaseModalCreateLiquidity.vue b/apps/app/src/components/modal/BaseModalCreateLiquidity.vue similarity index 98% rename from apps/campaign/src/components/modal/BaseModalCreateLiquidity.vue rename to apps/app/src/components/modal/BaseModalCreateLiquidity.vue index 08513051e..6c27abc4a 100644 --- a/apps/campaign/src/components/modal/BaseModalCreateLiquidity.vue +++ b/apps/app/src/components/modal/BaseModalCreateLiquidity.vue @@ -125,10 +125,10 @@ import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { BALANCER_POOL_ID, contractNetworks } from '../../config/constants'; import { parseUnits } from 'ethers/lib/utils'; -import { ChainId } from '@thxnetwork/sdk'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { ChainId } from '@thxnetwork/common/enums'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; import { BalancerSDK, Network } from '@balancer-labs/sdk'; -import { POLYGON_RPC } from '@thxnetwork/campaign/config/secrets'; +import { POLYGON_RPC } from '@thxnetwork/app/config/secrets'; export default defineComponent({ name: 'BaseModalCreateLiquidity', diff --git a/apps/campaign/src/components/modal/BaseModalDeposit.vue b/apps/app/src/components/modal/BaseModalDeposit.vue similarity index 96% rename from apps/campaign/src/components/modal/BaseModalDeposit.vue rename to apps/app/src/components/modal/BaseModalDeposit.vue index dcfed23be..2f2f9991b 100644 --- a/apps/campaign/src/components/modal/BaseModalDeposit.vue +++ b/apps/app/src/components/modal/BaseModalDeposit.vue @@ -52,11 +52,11 @@ import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { contractNetworks } from '../../config/constants'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { roundDownFixed } from '@thxnetwork/campaign/utils/price'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; -import { BigNumber } from 'ethers'; +import { roundDownFixed } from '@thxnetwork/app/utils/price'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; +import { BigNumber } from 'alchemy-sdk'; export default defineComponent({ name: 'BaseModalDeposit', diff --git a/apps/campaign/src/components/modal/BaseModalERC20Transfer.vue b/apps/app/src/components/modal/BaseModalERC20Transfer.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalERC20Transfer.vue rename to apps/app/src/components/modal/BaseModalERC20Transfer.vue diff --git a/apps/campaign/src/components/modal/BaseModalERC721Transfer.vue b/apps/app/src/components/modal/BaseModalERC721Transfer.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalERC721Transfer.vue rename to apps/app/src/components/modal/BaseModalERC721Transfer.vue diff --git a/apps/campaign/src/components/modal/BaseModalExternalURL.vue b/apps/app/src/components/modal/BaseModalExternalURL.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalExternalURL.vue rename to apps/app/src/components/modal/BaseModalExternalURL.vue diff --git a/apps/campaign/src/components/modal/BaseModalIncreaseAmount.vue b/apps/app/src/components/modal/BaseModalIncreaseAmount.vue similarity index 95% rename from apps/campaign/src/components/modal/BaseModalIncreaseAmount.vue rename to apps/app/src/components/modal/BaseModalIncreaseAmount.vue index 226d8dcbf..91c57019d 100644 --- a/apps/campaign/src/components/modal/BaseModalIncreaseAmount.vue +++ b/apps/app/src/components/modal/BaseModalIncreaseAmount.vue @@ -35,11 +35,11 @@ import { defineComponent } from 'vue'; import { mapStores } from 'pinia'; import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; import { contractNetworks } from '../../config/constants'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { ChainId } from '@thxnetwork/sdk'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { ChainId } from '@thxnetwork/common/enums'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; import { BigNumber } from 'ethers/lib/ethers'; export default defineComponent({ diff --git a/apps/campaign/src/components/modal/BaseModalIncreaseLockEnd.vue b/apps/app/src/components/modal/BaseModalIncreaseLockEnd.vue similarity index 94% rename from apps/campaign/src/components/modal/BaseModalIncreaseLockEnd.vue rename to apps/app/src/components/modal/BaseModalIncreaseLockEnd.vue index 5e3ade64f..b1e422e2c 100644 --- a/apps/campaign/src/components/modal/BaseModalIncreaseLockEnd.vue +++ b/apps/app/src/components/modal/BaseModalIncreaseLockEnd.vue @@ -26,8 +26,8 @@ import { defineComponent } from 'vue'; import { mapStores } from 'pinia'; import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; export default defineComponent({ name: 'BaseModalIncreaseLockEnd', diff --git a/apps/campaign/src/components/modal/BaseModalLogin.vue b/apps/app/src/components/modal/BaseModalLogin.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalLogin.vue rename to apps/app/src/components/modal/BaseModalLogin.vue diff --git a/apps/campaign/src/components/modal/BaseModalMembershipCreate.vue b/apps/app/src/components/modal/BaseModalMembershipCreate.vue similarity index 98% rename from apps/campaign/src/components/modal/BaseModalMembershipCreate.vue rename to apps/app/src/components/modal/BaseModalMembershipCreate.vue index 0c1dddd05..8db612185 100644 --- a/apps/campaign/src/components/modal/BaseModalMembershipCreate.vue +++ b/apps/app/src/components/modal/BaseModalMembershipCreate.vue @@ -135,13 +135,13 @@ import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { contractNetworks } from '../../config/constants'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; +import { chainList } from '@thxnetwork/app/utils/chains'; import { BigNumber } from 'ethers/lib/ethers'; import { format } from 'date-fns'; -import { roundDownFixed, toFiatPrice } from '@thxnetwork/campaign/utils/price'; -import { parseError } from '@thxnetwork/campaign/utils/toast'; +import { roundDownFixed, toFiatPrice } from '@thxnetwork/app/utils/price'; +import { parseError } from '@thxnetwork/app/utils/toast'; export default defineComponent({ name: 'BaseModalDeposit', diff --git a/apps/campaign/src/components/modal/BaseModalQuestEntry.vue b/apps/app/src/components/modal/BaseModalQuestEntry.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalQuestEntry.vue rename to apps/app/src/components/modal/BaseModalQuestEntry.vue diff --git a/apps/campaign/src/components/modal/BaseModalRewardPayment.vue b/apps/app/src/components/modal/BaseModalRewardPayment.vue similarity index 98% rename from apps/campaign/src/components/modal/BaseModalRewardPayment.vue rename to apps/app/src/components/modal/BaseModalRewardPayment.vue index 18fae2c2f..64b10e8cd 100644 --- a/apps/campaign/src/components/modal/BaseModalRewardPayment.vue +++ b/apps/app/src/components/modal/BaseModalRewardPayment.vue @@ -63,7 +63,7 @@ import { mapStores } from 'pinia'; import { useRewardStore } from '../../stores/Reward'; import { useAccountStore } from '../../stores/Account'; import { useWalletStore } from '../../stores/Wallet'; -import { RewardVariant } from '@thxnetwork/campaign/types/enums/rewards'; +import { RewardVariant } from '@thxnetwork/app/types/enums/rewards'; export default defineComponent({ name: 'BaseModalRewardPayment', diff --git a/apps/campaign/src/components/modal/BaseModalStake.vue b/apps/app/src/components/modal/BaseModalStake.vue similarity index 97% rename from apps/campaign/src/components/modal/BaseModalStake.vue rename to apps/app/src/components/modal/BaseModalStake.vue index c9cf23153..4c719a747 100644 --- a/apps/campaign/src/components/modal/BaseModalStake.vue +++ b/apps/app/src/components/modal/BaseModalStake.vue @@ -45,8 +45,8 @@ import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { contractNetworks } from '../../config/constants'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { ChainId } from '@thxnetwork/sdk'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { ChainId } from '@thxnetwork/common/enums'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; export default defineComponent({ name: 'BaseModalStake', diff --git a/apps/campaign/src/components/modal/BaseModalTokenSelect.vue b/apps/app/src/components/modal/BaseModalTokenSelect.vue similarity index 89% rename from apps/campaign/src/components/modal/BaseModalTokenSelect.vue rename to apps/app/src/components/modal/BaseModalTokenSelect.vue index b0a8b8f9c..e9177a0ad 100644 --- a/apps/campaign/src/components/modal/BaseModalTokenSelect.vue +++ b/apps/app/src/components/modal/BaseModalTokenSelect.vue @@ -40,11 +40,11 @@ import { defineComponent, PropType } from 'vue'; import { mapStores } from 'pinia'; import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; -import { toFiatPrice } from '@thxnetwork/campaign/utils/price'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { contractNetworks } from '@thxnetwork/campaign/config/constants'; -import { ChainId } from '@thxnetwork/sdk'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; +import { toFiatPrice } from '@thxnetwork/app/utils/price'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { contractNetworks } from '@thxnetwork/app/config/constants'; +import { ChainId } from '@thxnetwork/common/enums'; +import { chainList } from '@thxnetwork/app/utils/chains'; import { formatUnits } from 'ethers/lib/utils'; export default defineComponent({ diff --git a/apps/campaign/src/components/modal/BaseModalWallet.vue b/apps/app/src/components/modal/BaseModalWallet.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalWallet.vue rename to apps/app/src/components/modal/BaseModalWallet.vue diff --git a/apps/campaign/src/components/modal/BaseModalWalletConnect.vue b/apps/app/src/components/modal/BaseModalWalletConnect.vue similarity index 95% rename from apps/campaign/src/components/modal/BaseModalWalletConnect.vue rename to apps/app/src/components/modal/BaseModalWalletConnect.vue index 7ad6c3abb..7986f0741 100644 --- a/apps/campaign/src/components/modal/BaseModalWalletConnect.vue +++ b/apps/app/src/components/modal/BaseModalWalletConnect.vue @@ -46,9 +46,9 @@ import { chainList } from '../../utils/chains'; import { useWalletStore } from '../../stores/Wallet'; import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; -import { useAccountStore } from '@thxnetwork/campaign/stores/Account'; -import { ChainId } from '@thxnetwork/sdk'; -import { shortenAddress } from '@thxnetwork/campaign/utils/address'; +import { useAccountStore } from '@thxnetwork/app/stores/Account'; +import { ChainId } from '@thxnetwork/common/enums'; +import { shortenAddress } from '@thxnetwork/app/utils/address'; export default defineComponent({ name: 'BaseModalWalletConnect', diff --git a/apps/campaign/src/components/modal/BaseModalWalletCreate.vue b/apps/app/src/components/modal/BaseModalWalletCreate.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalWalletCreate.vue rename to apps/app/src/components/modal/BaseModalWalletCreate.vue diff --git a/apps/campaign/src/components/modal/BaseModalWalletRecovery.vue b/apps/app/src/components/modal/BaseModalWalletRecovery.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalWalletRecovery.vue rename to apps/app/src/components/modal/BaseModalWalletRecovery.vue diff --git a/apps/campaign/src/components/modal/BaseModalWalletSafe.vue b/apps/app/src/components/modal/BaseModalWalletSafe.vue similarity index 100% rename from apps/campaign/src/components/modal/BaseModalWalletSafe.vue rename to apps/app/src/components/modal/BaseModalWalletSafe.vue diff --git a/apps/campaign/src/components/modal/BaseModalWithdraw.vue b/apps/app/src/components/modal/BaseModalWithdraw.vue similarity index 94% rename from apps/campaign/src/components/modal/BaseModalWithdraw.vue rename to apps/app/src/components/modal/BaseModalWithdraw.vue index 21c20a63a..e52f7b79e 100644 --- a/apps/campaign/src/components/modal/BaseModalWithdraw.vue +++ b/apps/app/src/components/modal/BaseModalWithdraw.vue @@ -41,10 +41,10 @@ import { mapStores } from 'pinia'; import { useVeStore } from '../../stores/VE'; import { useWalletStore } from '../../stores/Wallet'; import { MAX_LOCK_TIME, contractNetworks } from '../../config/constants'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { calculatePenalty, toFiatPrice } from '@thxnetwork/campaign/utils/price'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { calculatePenalty, toFiatPrice } from '@thxnetwork/app/utils/price'; import { formatUnits } from 'ethers/lib/utils'; -import { WalletVariant } from '@thxnetwork/campaign/types/enums/accountVariant'; +import { WalletVariant } from '@thxnetwork/app/types/enums/accountVariant'; export default defineComponent({ name: 'BaseModalWithdraw', diff --git a/apps/campaign/src/components/navbar/BaseNavbar.vue b/apps/app/src/components/navbar/BaseNavbar.vue similarity index 100% rename from apps/campaign/src/components/navbar/BaseNavbar.vue rename to apps/app/src/components/navbar/BaseNavbar.vue diff --git a/apps/campaign/src/components/navbar/BaseNavbarPrimary.vue b/apps/app/src/components/navbar/BaseNavbarPrimary.vue similarity index 100% rename from apps/campaign/src/components/navbar/BaseNavbarPrimary.vue rename to apps/app/src/components/navbar/BaseNavbarPrimary.vue diff --git a/apps/campaign/src/components/navbar/BaseNavbarSecondary.vue b/apps/app/src/components/navbar/BaseNavbarSecondary.vue similarity index 100% rename from apps/campaign/src/components/navbar/BaseNavbarSecondary.vue rename to apps/app/src/components/navbar/BaseNavbarSecondary.vue diff --git a/apps/campaign/src/components/navbar/BaseNavbarTicker.vue b/apps/app/src/components/navbar/BaseNavbarTicker.vue similarity index 100% rename from apps/campaign/src/components/navbar/BaseNavbarTicker.vue rename to apps/app/src/components/navbar/BaseNavbarTicker.vue diff --git a/apps/campaign/src/components/navbar/BaseNavbarTop.vue b/apps/app/src/components/navbar/BaseNavbarTop.vue similarity index 100% rename from apps/campaign/src/components/navbar/BaseNavbarTop.vue rename to apps/app/src/components/navbar/BaseNavbarTop.vue diff --git a/apps/campaign/src/components/tabs/BaseTabDeposit.vue b/apps/app/src/components/tabs/BaseTabDeposit.vue similarity index 95% rename from apps/campaign/src/components/tabs/BaseTabDeposit.vue rename to apps/app/src/components/tabs/BaseTabDeposit.vue index 12da1a2f2..56f09000f 100644 --- a/apps/campaign/src/components/tabs/BaseTabDeposit.vue +++ b/apps/app/src/components/tabs/BaseTabDeposit.vue @@ -41,11 +41,11 @@ import { useLiquidityStore } from '../../stores/Liquidity'; import { useVeStore } from '../../stores/VE'; import { contractNetworks } from '../../config/constants'; import { NinetyDaysInMs, getThursdaysUntilTimestamp } from '../../utils/date'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { useAuthStore } from '@thxnetwork/campaign/stores/Auth'; +import { useAuthStore } from '@thxnetwork/app/stores/Auth'; import { BigNumber } from 'ethers/lib/ethers'; -import { parseError } from '@thxnetwork/campaign/utils/toast'; +import { parseError } from '@thxnetwork/app/utils/toast'; export default defineComponent({ name: 'BaseTabDeposit', diff --git a/apps/campaign/src/components/tabs/BaseTabLiquidity.vue b/apps/app/src/components/tabs/BaseTabLiquidity.vue similarity index 98% rename from apps/campaign/src/components/tabs/BaseTabLiquidity.vue rename to apps/app/src/components/tabs/BaseTabLiquidity.vue index a646f2768..1f2ed1686 100644 --- a/apps/campaign/src/components/tabs/BaseTabLiquidity.vue +++ b/apps/app/src/components/tabs/BaseTabLiquidity.vue @@ -217,12 +217,12 @@ import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { useVeStore } from '../../stores/VE'; import { contractNetworks } from '../../config/constants'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { useAuthStore } from '@thxnetwork/campaign/stores/Auth'; -import { chainList } from '@thxnetwork/campaign/utils/chains'; +import { useAuthStore } from '@thxnetwork/app/stores/Auth'; +import { chainList } from '@thxnetwork/app/utils/chains'; import { BigNumber } from 'ethers/lib/ethers'; -import { parseError } from '@thxnetwork/campaign/utils/toast'; +import { parseError } from '@thxnetwork/app/utils/toast'; export default defineComponent({ name: 'BaseTabLiquidity', diff --git a/apps/campaign/src/components/tabs/BaseTabWalletVariant.vue b/apps/app/src/components/tabs/BaseTabWalletVariant.vue similarity index 100% rename from apps/campaign/src/components/tabs/BaseTabWalletVariant.vue rename to apps/app/src/components/tabs/BaseTabWalletVariant.vue diff --git a/apps/campaign/src/components/tabs/BaseTabWalletWalletConnect.vue b/apps/app/src/components/tabs/BaseTabWalletWalletConnect.vue similarity index 98% rename from apps/campaign/src/components/tabs/BaseTabWalletWalletConnect.vue rename to apps/app/src/components/tabs/BaseTabWalletWalletConnect.vue index e7605fba3..c4aa1f357 100644 --- a/apps/campaign/src/components/tabs/BaseTabWalletWalletConnect.vue +++ b/apps/app/src/components/tabs/BaseTabWalletWalletConnect.vue @@ -24,7 +24,7 @@ import { useWalletStore, walletLogoMap } from '../../stores/Wallet'; import { useAccountStore } from '../../stores/Account'; import { useAuthStore } from '../../stores/Auth'; import { WalletVariant } from '../../types/enums/accountVariant'; -import { shortenAddress } from '@thxnetwork/campaign/utils/address'; +import { shortenAddress } from '@thxnetwork/app/utils/address'; import poll from 'promise-poller'; export default defineComponent({ diff --git a/apps/campaign/src/components/tabs/BaseTabWalletWeb3Auth.vue b/apps/app/src/components/tabs/BaseTabWalletWeb3Auth.vue similarity index 100% rename from apps/campaign/src/components/tabs/BaseTabWalletWeb3Auth.vue rename to apps/app/src/components/tabs/BaseTabWalletWeb3Auth.vue diff --git a/apps/campaign/src/components/tabs/BaseTabWithdraw.vue b/apps/app/src/components/tabs/BaseTabWithdraw.vue similarity index 98% rename from apps/campaign/src/components/tabs/BaseTabWithdraw.vue rename to apps/app/src/components/tabs/BaseTabWithdraw.vue index 465a7e0f4..25544c89f 100644 --- a/apps/campaign/src/components/tabs/BaseTabWithdraw.vue +++ b/apps/app/src/components/tabs/BaseTabWithdraw.vue @@ -61,7 +61,7 @@ import { useVeStore } from '../../stores/VE'; import { format, differenceInDays } from 'date-fns'; import { contractNetworks } from '../../config/constants'; import { fromWei } from 'web3-utils'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; export default defineComponent({ name: 'BaseTabWithdraw', diff --git a/apps/campaign/src/config/constants.ts b/apps/app/src/config/constants.ts similarity index 100% rename from apps/campaign/src/config/constants.ts rename to apps/app/src/config/constants.ts diff --git a/apps/campaign/src/config/secrets.ts b/apps/app/src/config/secrets.ts similarity index 100% rename from apps/campaign/src/config/secrets.ts rename to apps/app/src/config/secrets.ts diff --git a/apps/campaign/src/environments/environment.prod.ts b/apps/app/src/environments/environment.prod.ts similarity index 100% rename from apps/campaign/src/environments/environment.prod.ts rename to apps/app/src/environments/environment.prod.ts diff --git a/apps/campaign/src/environments/environment.ts b/apps/app/src/environments/environment.ts similarity index 100% rename from apps/campaign/src/environments/environment.ts rename to apps/app/src/environments/environment.ts diff --git a/apps/campaign/src/main.ts b/apps/app/src/main.ts similarity index 92% rename from apps/campaign/src/main.ts rename to apps/app/src/main.ts index e99b9c5d2..d2e3b05b6 100644 --- a/apps/campaign/src/main.ts +++ b/apps/app/src/main.ts @@ -1,13 +1,13 @@ import { BootstrapVueNext, vBTooltip } from 'bootstrap-vue-next'; import { createApp } from 'vue'; import { createPinia } from 'pinia'; -import { Sentry } from '@thxnetwork/common/lib/sentry'; +import { Sentry } from '@thxnetwork/common/sentry'; import { GCLOUD_RECAPTCHA_SITE_KEY, MODE, API_URL, MIXPANEL_TOKEN, AUTH_URL, WIDGET_URL } from './config/secrets'; import App from './App.vue'; import VueClipboard from 'vue3-clipboard'; import Vue3Toastify from 'vue3-toastify'; import router from './router'; -import Mixpanel from '@thxnetwork/common/lib/mixpanel'; +import Mixpanel from '@thxnetwork/common/mixpanel'; import './scss/main.scss'; diff --git a/apps/campaign/src/polyfills.ts b/apps/app/src/polyfills.ts similarity index 100% rename from apps/campaign/src/polyfills.ts rename to apps/app/src/polyfills.ts diff --git a/apps/campaign/src/router/index.ts b/apps/app/src/router/index.ts similarity index 100% rename from apps/campaign/src/router/index.ts rename to apps/app/src/router/index.ts diff --git a/apps/campaign/src/scss/_badge.scss b/apps/app/src/scss/_badge.scss similarity index 100% rename from apps/campaign/src/scss/_badge.scss rename to apps/app/src/scss/_badge.scss diff --git a/apps/campaign/src/scss/_blockquote.scss b/apps/app/src/scss/_blockquote.scss similarity index 100% rename from apps/campaign/src/scss/_blockquote.scss rename to apps/app/src/scss/_blockquote.scss diff --git a/apps/campaign/src/scss/_card.scss b/apps/app/src/scss/_card.scss similarity index 100% rename from apps/campaign/src/scss/_card.scss rename to apps/app/src/scss/_card.scss diff --git a/apps/campaign/src/scss/_forms.scss b/apps/app/src/scss/_forms.scss similarity index 100% rename from apps/campaign/src/scss/_forms.scss rename to apps/app/src/scss/_forms.scss diff --git a/apps/campaign/src/scss/_gradients.scss b/apps/app/src/scss/_gradients.scss similarity index 100% rename from apps/campaign/src/scss/_gradients.scss rename to apps/app/src/scss/_gradients.scss diff --git a/apps/campaign/src/scss/_modals.scss b/apps/app/src/scss/_modals.scss similarity index 100% rename from apps/campaign/src/scss/_modals.scss rename to apps/app/src/scss/_modals.scss diff --git a/apps/campaign/src/scss/_navbar.scss b/apps/app/src/scss/_navbar.scss similarity index 100% rename from apps/campaign/src/scss/_navbar.scss rename to apps/app/src/scss/_navbar.scss diff --git a/apps/campaign/src/scss/_reset.scss b/apps/app/src/scss/_reset.scss similarity index 100% rename from apps/campaign/src/scss/_reset.scss rename to apps/app/src/scss/_reset.scss diff --git a/apps/campaign/src/scss/_tables.scss b/apps/app/src/scss/_tables.scss similarity index 100% rename from apps/campaign/src/scss/_tables.scss rename to apps/app/src/scss/_tables.scss diff --git a/apps/campaign/src/scss/_tabs.scss b/apps/app/src/scss/_tabs.scss similarity index 100% rename from apps/campaign/src/scss/_tabs.scss rename to apps/app/src/scss/_tabs.scss diff --git a/apps/campaign/src/scss/_theme.scss b/apps/app/src/scss/_theme.scss similarity index 100% rename from apps/campaign/src/scss/_theme.scss rename to apps/app/src/scss/_theme.scss diff --git a/apps/campaign/src/scss/_toasts.scss b/apps/app/src/scss/_toasts.scss similarity index 100% rename from apps/campaign/src/scss/_toasts.scss rename to apps/app/src/scss/_toasts.scss diff --git a/apps/campaign/src/scss/_variables.scss b/apps/app/src/scss/_variables.scss similarity index 100% rename from apps/campaign/src/scss/_variables.scss rename to apps/app/src/scss/_variables.scss diff --git a/apps/campaign/src/scss/main.scss b/apps/app/src/scss/main.scss similarity index 100% rename from apps/campaign/src/scss/main.scss rename to apps/app/src/scss/main.scss diff --git a/apps/campaign/src/shims-vue.d.ts b/apps/app/src/shims-vue.d.ts similarity index 100% rename from apps/campaign/src/shims-vue.d.ts rename to apps/app/src/shims-vue.d.ts diff --git a/apps/campaign/src/stores/Account.ts b/apps/app/src/stores/Account.ts similarity index 99% rename from apps/campaign/src/stores/Account.ts rename to apps/app/src/stores/Account.ts index a0d9eab20..743fe426c 100644 --- a/apps/campaign/src/stores/Account.ts +++ b/apps/app/src/stores/Account.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia'; -import { track } from '@thxnetwork/common/lib/mixpanel'; -import { THXBrowserClient } from '@thxnetwork/sdk'; +import { track } from '@thxnetwork/common/mixpanel'; +import { THXBrowserClient } from '@thxnetwork/sdk/clients'; import { API_URL, AUTH_URL, CLIENT_ID, WIDGET_URL } from '../config/secrets'; import { DEFAULT_COLORS, DEFAULT_ELEMENTS, getStyles } from '../utils/theme'; import { BREAKPOINT_LG } from '../config/constants'; diff --git a/apps/campaign/src/stores/Auth.ts b/apps/app/src/stores/Auth.ts similarity index 99% rename from apps/campaign/src/stores/Auth.ts rename to apps/app/src/stores/Auth.ts index daae0bca0..cadae11a5 100644 --- a/apps/campaign/src/stores/Auth.ts +++ b/apps/app/src/stores/Auth.ts @@ -5,7 +5,7 @@ import { tKey } from '../utils/tkey'; import { useAccountStore } from './Account'; import { User, UserManager, WebStorageStateStore } from 'oidc-client-ts'; import { Wallet } from '@ethersproject/wallet'; -import { track } from '@thxnetwork/common/lib/mixpanel'; +import { track } from '@thxnetwork/common/mixpanel'; import poll from 'promise-poller'; import { useVeStore } from './VE'; import { useWalletStore } from './Wallet'; diff --git a/apps/campaign/src/stores/Liquidity.ts b/apps/app/src/stores/Liquidity.ts similarity index 99% rename from apps/campaign/src/stores/Liquidity.ts rename to apps/app/src/stores/Liquidity.ts index 7450e1ab5..dcbf4b933 100644 --- a/apps/campaign/src/stores/Liquidity.ts +++ b/apps/app/src/stores/Liquidity.ts @@ -4,9 +4,9 @@ import { useAccountStore } from './Account'; import { useWalletStore } from './Wallet'; import { BALANCER_POOL_ID, contractNetworks } from '../config/constants'; import { WalletVariant } from '../types/enums/accountVariant'; -import { BigNumber } from 'ethers'; +import { BigNumber } from 'alchemy-sdk'; import { PoolWithMethods } from '@balancer-labs/sdk'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; type TCreateLiquidityOptions = { thxAmountInWei: string; diff --git a/apps/campaign/src/stores/QRCode.ts b/apps/app/src/stores/QRCode.ts similarity index 95% rename from apps/campaign/src/stores/QRCode.ts rename to apps/app/src/stores/QRCode.ts index a3ab6a086..121ab2eca 100644 --- a/apps/campaign/src/stores/QRCode.ts +++ b/apps/app/src/stores/QRCode.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia'; import { useAccountStore } from './Account'; -import { track } from '@thxnetwork/common/lib/mixpanel'; +import { track } from '@thxnetwork/common/mixpanel'; export const useQRCodeStore = defineStore('qrcode', { state: (): TQRCodeState => ({ diff --git a/apps/campaign/src/stores/Quest.ts b/apps/app/src/stores/Quest.ts similarity index 97% rename from apps/campaign/src/stores/Quest.ts rename to apps/app/src/stores/Quest.ts index dd73fcf9a..068c4a802 100644 --- a/apps/campaign/src/stores/Quest.ts +++ b/apps/app/src/stores/Quest.ts @@ -1,7 +1,7 @@ import { defineStore } from 'pinia'; import { useAccountStore } from './Account'; -import { track } from '@thxnetwork/common/lib/mixpanel'; -import { QuestVariant } from '@thxnetwork/sdk'; +import { track } from '@thxnetwork/common/mixpanel'; +import { QuestVariant } from '@thxnetwork/common/enums'; import { GCLOUD_RECAPTCHA_SITE_KEY } from '../config/secrets'; export const useQuestStore = defineStore('quest', { diff --git a/apps/campaign/src/stores/Reward.ts b/apps/app/src/stores/Reward.ts similarity index 97% rename from apps/campaign/src/stores/Reward.ts rename to apps/app/src/stores/Reward.ts index 37c12da7d..d18479c1e 100644 --- a/apps/campaign/src/stores/Reward.ts +++ b/apps/app/src/stores/Reward.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia'; -import { track } from '@thxnetwork/common/lib/mixpanel'; +import { track } from '@thxnetwork/common/mixpanel'; import { toNumber } from '../utils/quests'; import { useAccountStore } from './Account'; import { RewardVariant } from '../types/enums/rewards'; diff --git a/apps/campaign/src/stores/Snapshot.ts b/apps/app/src/stores/Snapshot.ts similarity index 100% rename from apps/campaign/src/stores/Snapshot.ts rename to apps/app/src/stores/Snapshot.ts diff --git a/apps/campaign/src/stores/VE.ts b/apps/app/src/stores/VE.ts similarity index 99% rename from apps/campaign/src/stores/VE.ts rename to apps/app/src/stores/VE.ts index d1ac069d4..beed6dbd5 100644 --- a/apps/campaign/src/stores/VE.ts +++ b/apps/app/src/stores/VE.ts @@ -1,11 +1,11 @@ import { defineStore } from 'pinia'; import { useAccountStore } from './Account'; import { useWalletStore } from './Wallet'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; import { MODE } from '../config/secrets'; import { WalletVariant } from '../types/enums/accountVariant'; import { contractNetworks } from '../config/constants'; -import { BigNumber } from 'ethers'; +import { BigNumber } from 'alchemy-sdk'; import poll, { CANCEL_TOKEN } from 'promise-poller'; export function getChainId() { diff --git a/apps/campaign/src/stores/Wallet.ts b/apps/app/src/stores/Wallet.ts similarity index 99% rename from apps/campaign/src/stores/Wallet.ts rename to apps/app/src/stores/Wallet.ts index 59d3b4bf1..18f4f86a7 100644 --- a/apps/campaign/src/stores/Wallet.ts +++ b/apps/app/src/stores/Wallet.ts @@ -1,11 +1,11 @@ import { defineStore } from 'pinia'; import { useAccountStore } from './Account'; -import { track } from '@thxnetwork/common/lib/mixpanel'; +import { track } from '@thxnetwork/common/mixpanel'; import { HARDHAT_RPC, POLYGON_RPC } from '../config/secrets'; import { useAuthStore } from './Auth'; import { EthersAdapter, SafeConfig } from '@safe-global/protocol-kit'; import { ethers } from 'ethers'; -import { ChainId } from '@thxnetwork/sdk/src/lib/types/enums/ChainId'; +import { ChainId } from '@thxnetwork/common/enums'; import { WalletVariant } from '../types/enums/accountVariant'; import { AUTH_URL, WALLET_CONNECT_PROJECT_ID, WIDGET_URL } from '../config/secrets'; import { createWeb3Modal, defaultWagmiConfig } from '@web3modal/wagmi'; diff --git a/apps/campaign/src/types/enums/accessTokenKind.ts b/apps/app/src/types/enums/accessTokenKind.ts similarity index 100% rename from apps/campaign/src/types/enums/accessTokenKind.ts rename to apps/app/src/types/enums/accessTokenKind.ts diff --git a/apps/campaign/src/types/enums/accountVariant.ts b/apps/app/src/types/enums/accountVariant.ts similarity index 100% rename from apps/campaign/src/types/enums/accountVariant.ts rename to apps/app/src/types/enums/accountVariant.ts diff --git a/apps/campaign/src/types/enums/nft.ts b/apps/app/src/types/enums/nft.ts similarity index 100% rename from apps/campaign/src/types/enums/nft.ts rename to apps/app/src/types/enums/nft.ts diff --git a/apps/campaign/src/types/enums/rewards.ts b/apps/app/src/types/enums/rewards.ts similarity index 100% rename from apps/campaign/src/types/enums/rewards.ts rename to apps/app/src/types/enums/rewards.ts diff --git a/apps/campaign/src/types/interfaces/account.d.ts b/apps/app/src/types/interfaces/account.d.ts similarity index 100% rename from apps/campaign/src/types/interfaces/account.d.ts rename to apps/app/src/types/interfaces/account.d.ts diff --git a/apps/campaign/src/types/interfaces/chains.d.ts b/apps/app/src/types/interfaces/chains.d.ts similarity index 100% rename from apps/campaign/src/types/interfaces/chains.d.ts rename to apps/app/src/types/interfaces/chains.d.ts diff --git a/apps/campaign/src/types/interfaces/qrcodes.d.ts b/apps/app/src/types/interfaces/qrcodes.d.ts similarity index 100% rename from apps/campaign/src/types/interfaces/qrcodes.d.ts rename to apps/app/src/types/interfaces/qrcodes.d.ts diff --git a/apps/campaign/src/types/interfaces/quests.d.ts b/apps/app/src/types/interfaces/quests.d.ts similarity index 100% rename from apps/campaign/src/types/interfaces/quests.d.ts rename to apps/app/src/types/interfaces/quests.d.ts diff --git a/apps/campaign/src/types/interfaces/rewards.d.ts b/apps/app/src/types/interfaces/rewards.d.ts similarity index 100% rename from apps/campaign/src/types/interfaces/rewards.d.ts rename to apps/app/src/types/interfaces/rewards.d.ts diff --git a/apps/campaign/src/types/interfaces/wallet.d.ts b/apps/app/src/types/interfaces/wallet.d.ts similarity index 100% rename from apps/campaign/src/types/interfaces/wallet.d.ts rename to apps/app/src/types/interfaces/wallet.d.ts diff --git a/apps/campaign/src/types/snapshot.d.ts b/apps/app/src/types/snapshot.d.ts similarity index 100% rename from apps/campaign/src/types/snapshot.d.ts rename to apps/app/src/types/snapshot.d.ts diff --git a/apps/campaign/src/utils/address.ts b/apps/app/src/utils/address.ts similarity index 100% rename from apps/campaign/src/utils/address.ts rename to apps/app/src/utils/address.ts diff --git a/apps/campaign/src/utils/chains.ts b/apps/app/src/utils/chains.ts similarity index 97% rename from apps/campaign/src/utils/chains.ts rename to apps/app/src/utils/chains.ts index 2a3ebb9cf..d303a6b58 100644 --- a/apps/campaign/src/utils/chains.ts +++ b/apps/app/src/utils/chains.ts @@ -5,7 +5,7 @@ import ImgLogoPolygon from '../assets/thx_logo_polygon.svg'; import ImgLogoHardhat from '../assets/thx_logo_hardhat.svg'; import ImgLogoLinea from '../assets/thx_logo_linea.svg'; import { arbitrum, mainnet, bsc, polygon, hardhat, polygonZkEvm, linea } from '@wagmi/core/chains'; -import { ChainId } from '@thxnetwork/sdk'; +import { ChainId } from '@thxnetwork/common/enums'; const chainList: { [chainId: number]: ChainInfo } = { [ChainId.Ethereum]: { diff --git a/apps/campaign/src/utils/date.ts b/apps/app/src/utils/date.ts similarity index 100% rename from apps/campaign/src/utils/date.ts rename to apps/app/src/utils/date.ts diff --git a/apps/campaign/src/utils/decode-html.ts b/apps/app/src/utils/decode-html.ts similarity index 100% rename from apps/campaign/src/utils/decode-html.ts rename to apps/app/src/utils/decode-html.ts diff --git a/apps/campaign/src/utils/ga.ts b/apps/app/src/utils/ga.ts similarity index 100% rename from apps/campaign/src/utils/ga.ts rename to apps/app/src/utils/ga.ts diff --git a/apps/campaign/src/utils/popup.ts b/apps/app/src/utils/popup.ts similarity index 100% rename from apps/campaign/src/utils/popup.ts rename to apps/app/src/utils/popup.ts diff --git a/apps/campaign/src/utils/price.ts b/apps/app/src/utils/price.ts similarity index 100% rename from apps/campaign/src/utils/price.ts rename to apps/app/src/utils/price.ts diff --git a/apps/campaign/src/utils/quests.ts b/apps/app/src/utils/quests.ts similarity index 100% rename from apps/campaign/src/utils/quests.ts rename to apps/app/src/utils/quests.ts diff --git a/apps/campaign/src/utils/rand-hex.ts b/apps/app/src/utils/rand-hex.ts similarity index 100% rename from apps/campaign/src/utils/rand-hex.ts rename to apps/app/src/utils/rand-hex.ts diff --git a/apps/campaign/src/utils/social.ts b/apps/app/src/utils/social.ts similarity index 100% rename from apps/campaign/src/utils/social.ts rename to apps/app/src/utils/social.ts diff --git a/apps/campaign/src/utils/sort.ts b/apps/app/src/utils/sort.ts similarity index 100% rename from apps/campaign/src/utils/sort.ts rename to apps/app/src/utils/sort.ts diff --git a/apps/campaign/src/utils/theme.ts b/apps/app/src/utils/theme.ts similarity index 100% rename from apps/campaign/src/utils/theme.ts rename to apps/app/src/utils/theme.ts diff --git a/apps/campaign/src/utils/tkey.ts b/apps/app/src/utils/tkey.ts similarity index 100% rename from apps/campaign/src/utils/tkey.ts rename to apps/app/src/utils/tkey.ts diff --git a/apps/campaign/src/utils/toast.ts b/apps/app/src/utils/toast.ts similarity index 100% rename from apps/campaign/src/utils/toast.ts rename to apps/app/src/utils/toast.ts diff --git a/apps/campaign/src/views/Campaign.vue b/apps/app/src/views/Campaign.vue similarity index 98% rename from apps/campaign/src/views/Campaign.vue rename to apps/app/src/views/Campaign.vue index f0bda3073..07f656304 100644 --- a/apps/campaign/src/views/Campaign.vue +++ b/apps/app/src/views/Campaign.vue @@ -17,7 +17,7 @@ </template> <script lang="ts"> import { mapStores } from 'pinia'; -import { track } from '@thxnetwork/common/lib/mixpanel'; +import { track } from '@thxnetwork/common/mixpanel'; import { defineComponent } from 'vue'; import { GTM } from '../config/secrets'; import { useAuthStore } from '../stores/Auth'; diff --git a/apps/campaign/src/views/Discovery.vue b/apps/app/src/views/Discovery.vue similarity index 79% rename from apps/campaign/src/views/Discovery.vue rename to apps/app/src/views/Discovery.vue index b817a0e8f..52d51b27c 100644 --- a/apps/campaign/src/views/Discovery.vue +++ b/apps/app/src/views/Discovery.vue @@ -12,9 +12,9 @@ <script lang="ts"> import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { useWalletStore } from '@thxnetwork/campaign/stores/Wallet'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { useWalletStore } from '@thxnetwork/app/stores/Wallet'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; export default defineComponent({ name: 'Discovery', diff --git a/apps/campaign/src/views/Wallets.vue b/apps/app/src/views/Wallets.vue similarity index 100% rename from apps/campaign/src/views/Wallets.vue rename to apps/app/src/views/Wallets.vue diff --git a/apps/campaign/src/views/campaign/About.vue b/apps/app/src/views/campaign/About.vue similarity index 100% rename from apps/campaign/src/views/campaign/About.vue rename to apps/app/src/views/campaign/About.vue diff --git a/apps/campaign/src/views/campaign/Collect.vue b/apps/app/src/views/campaign/Collect.vue similarity index 100% rename from apps/campaign/src/views/campaign/Collect.vue rename to apps/app/src/views/campaign/Collect.vue diff --git a/apps/campaign/src/views/campaign/Identities.vue b/apps/app/src/views/campaign/Identities.vue similarity index 100% rename from apps/campaign/src/views/campaign/Identities.vue rename to apps/app/src/views/campaign/Identities.vue diff --git a/apps/campaign/src/views/campaign/Quests.vue b/apps/app/src/views/campaign/Quests.vue similarity index 100% rename from apps/campaign/src/views/campaign/Quests.vue rename to apps/app/src/views/campaign/Quests.vue diff --git a/apps/campaign/src/views/campaign/Ranking.vue b/apps/app/src/views/campaign/Ranking.vue similarity index 100% rename from apps/campaign/src/views/campaign/Ranking.vue rename to apps/app/src/views/campaign/Ranking.vue diff --git a/apps/campaign/src/views/campaign/Rewards.vue b/apps/app/src/views/campaign/Rewards.vue similarity index 100% rename from apps/campaign/src/views/campaign/Rewards.vue rename to apps/app/src/views/campaign/Rewards.vue diff --git a/apps/campaign/src/views/campaign/Signin.vue b/apps/app/src/views/campaign/Signin.vue similarity index 100% rename from apps/campaign/src/views/campaign/Signin.vue rename to apps/app/src/views/campaign/Signin.vue diff --git a/apps/campaign/src/views/discovery/Community.vue b/apps/app/src/views/discovery/Community.vue similarity index 98% rename from apps/campaign/src/views/discovery/Community.vue rename to apps/app/src/views/discovery/Community.vue index c1494a799..93022719e 100644 --- a/apps/campaign/src/views/discovery/Community.vue +++ b/apps/app/src/views/discovery/Community.vue @@ -47,7 +47,7 @@ import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; import { useSnapshotStore } from '../../stores/Snapshot'; import { marked } from 'marked'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; import { formatUnits } from 'ethers/lib/utils'; import imgJumbotron from '../../assets/thx_token_governance.png'; diff --git a/apps/campaign/src/views/discovery/Earn.vue b/apps/app/src/views/discovery/Earn.vue similarity index 81% rename from apps/campaign/src/views/discovery/Earn.vue rename to apps/app/src/views/discovery/Earn.vue index b9a530598..693d79182 100644 --- a/apps/campaign/src/views/discovery/Earn.vue +++ b/apps/app/src/views/discovery/Earn.vue @@ -39,30 +39,7 @@ </div> </template> <template #secondary> - <b-card class="border-0 gradient-shadow-xl" style="min-height: 415px"> - <b-tabs v-model="tabIndex" pills justified content-class="mt-3" nav-wrapper-class="text-white"> - <b-tab> - <template #title> - <i class="fas fa-balance-scale me-1" /> - Liquidity - </template> - <hr /> - <BaseTabLiquidity @change-tab="tabIndex = $event" /> - </b-tab> - <b-tab> - <template #title> - <i class="fas fa-id-card me-1" /> - Membership - </template> - <hr /> - <BaseTabDeposit - v-if="veStore.lock && !Number(veStore.lock.amount)" - @change-tab="tabIndex = $event" - /> - <BaseTabWithdraw v-else @change-tab="tabIndex = $event" /> - </b-tab> - </b-tabs> - </b-card> + <BaseCardMembership :tab-index="0" /> </template> </BaseCardHeader> <b-container class="mb-5"> @@ -84,9 +61,9 @@ import { useAccountStore } from '../../stores/Account'; import { useWalletStore } from '../../stores/Wallet'; import { useLiquidityStore } from '../../stores/Liquidity'; import { useVeStore } from '../../stores/VE'; -import { roundUpFixed, toFiatPrice } from '@thxnetwork/campaign/utils/price'; +import { roundUpFixed, toFiatPrice } from '@thxnetwork/app/utils/price'; import { formatUnits, parseUnits } from 'ethers/lib/utils'; -import { BigNumber } from 'ethers'; +import { BigNumber } from 'alchemy-sdk'; import { startOfWeek, addWeeks, format, eachWeekOfInterval } from 'date-fns'; export default defineComponent({ diff --git a/apps/campaign/src/views/discovery/Home.vue b/apps/app/src/views/discovery/Home.vue similarity index 100% rename from apps/campaign/src/views/discovery/Home.vue rename to apps/app/src/views/discovery/Home.vue diff --git a/apps/campaign/src/views/discovery/Learn.vue b/apps/app/src/views/discovery/Learn.vue similarity index 95% rename from apps/campaign/src/views/discovery/Learn.vue rename to apps/app/src/views/discovery/Learn.vue index ef321da7f..ec89a014e 100644 --- a/apps/campaign/src/views/discovery/Learn.vue +++ b/apps/app/src/views/discovery/Learn.vue @@ -50,7 +50,7 @@ </template> <script lang="ts"> -import { useQuestStore } from '@thxnetwork/campaign/stores/Quest'; +import { useQuestStore } from '@thxnetwork/app/stores/Quest'; import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; import { questComponentMap } from '../../utils/quests'; @@ -62,8 +62,8 @@ import BaseCardQuestDaily from '../../components/card/BaseCardQuestDaily.vue'; import BaseCardQuestWeb3 from '../../components/card/BaseCardQuestWeb3.vue'; import BaseCardQuestGitcoin from '../../components/card/BaseCardQuestGitcoin.vue'; import BaseCardQuestWebhook from '../../components/card/BaseCardQuestWebhook.vue'; -import { useAccountStore } from '@thxnetwork/campaign/stores/Account'; -import { useAuthStore } from '@thxnetwork/campaign/stores/Auth'; +import { useAccountStore } from '@thxnetwork/app/stores/Account'; +import { useAuthStore } from '@thxnetwork/app/stores/Auth'; export default defineComponent({ name: 'Learn', diff --git a/apps/campaign/src/views/discovery/Members.vue b/apps/app/src/views/discovery/Members.vue similarity index 93% rename from apps/campaign/src/views/discovery/Members.vue rename to apps/app/src/views/discovery/Members.vue index 5704b3514..8424e21be 100644 --- a/apps/campaign/src/views/discovery/Members.vue +++ b/apps/app/src/views/discovery/Members.vue @@ -11,10 +11,7 @@ </p> </template> <template #secondary> - <BaseCardMembershipOnboarding v-if="!Number(formatUnits(veStore.lock.amount, 18))" /> - <div v-else class="h-100 d-flex align-items-center justify-content-center"> - <BaseCardMembership /> - </div> + <BaseCardMembership :tab-index="1" /> </template> </BaseCardHeader> <b-container class="mt-5"> @@ -70,8 +67,8 @@ </template> <script lang="ts"> -import { useLiquidityStore } from '@thxnetwork/campaign/stores/Liquidity'; -import { useVeStore } from '@thxnetwork/campaign/stores/VE'; +import { useLiquidityStore } from '@thxnetwork/app/stores/Liquidity'; +import { useVeStore } from '@thxnetwork/app/stores/VE'; import { formatUnits } from 'ethers/lib/utils'; import { mapStores } from 'pinia'; import { defineComponent } from 'vue'; diff --git a/apps/campaign/src/vite-env.d.ts b/apps/app/src/vite-env.d.ts similarity index 100% rename from apps/campaign/src/vite-env.d.ts rename to apps/app/src/vite-env.d.ts diff --git a/apps/campaign/tsconfig.json b/apps/app/tsconfig.json similarity index 80% rename from apps/campaign/tsconfig.json rename to apps/app/tsconfig.json index 152b68801..a29abe707 100644 --- a/apps/campaign/tsconfig.json +++ b/apps/app/tsconfig.json @@ -6,7 +6,8 @@ "components.d.ts", "src/types/**/*.d.ts", "node_modules/**/*.d.ts", - "../../libs/common/**/*" + "../../libs/common/**/*", + "../../libs/sdk/**/*" ], "exclude": ["**/*.spec.ts", "**/*.test.ts"] } diff --git a/apps/campaign/vite.config.ts b/apps/app/vite.config.ts similarity index 89% rename from apps/campaign/vite.config.ts rename to apps/app/vite.config.ts index c0d169f25..544ac22c3 100644 --- a/apps/campaign/vite.config.ts +++ b/apps/app/vite.config.ts @@ -33,8 +33,9 @@ const config: UserConfigExport = { }, resolve: { alias: [ - { find: '@thxnetwork/campaign', replacement: path.resolve(__dirname, './src') }, - { find: '@thxnetwork/common', replacement: path.resolve(__dirname, '../../libs/common/src') }, + { find: '@thxnetwork/app', replacement: path.resolve(__dirname, './src') }, + { find: '@thxnetwork/common', replacement: path.resolve(__dirname, '../../libs/common/src/lib') }, + { find: '@thxnetwork/sdk', replacement: path.resolve(__dirname, '../../libs/sdk/src/lib') }, ], }, optimizeDeps: { diff --git a/apps/auth/.env.ci b/apps/auth/.env.ci new file mode 100644 index 000000000..d50e39363 --- /dev/null +++ b/apps/auth/.env.ci @@ -0,0 +1,7 @@ +MONGODB_URI=mongodb://root:root@mongo:27017/auth?authSource=admin&ssl=false +MONGODB_URI_TEST_OVERRIDE=mongodb://root:root@mongo:27017/auth_test?authSource=admin&ssl=false +AUTH_URL=http://localhost:3030 +AUTH_URL_TEST_OVERRIDE=http://localhost:3030 +CWD="/usr/src/app/apps/auth/src/" +LOCAL_CERT= +LOCAL_CERT_KEY= \ No newline at end of file diff --git a/apps/auth/.env.example b/apps/auth/.env.example new file mode 100644 index 000000000..98601cce1 --- /dev/null +++ b/apps/auth/.env.example @@ -0,0 +1,46 @@ +# Docker MongoDB URI +MONGODB_URI=mongodb://root:root@localhost:27017/auth?authSource=admin&ssl=false +MONGODB_URI_TEST_OVERRIDE=mongodb://root:root@localhost:27017/auth_test?authSource=admin&ssl=false +MONGODB_NAME=auth +MONGODB_USER=root +MONGODB_PASSWORD=root + +# Origins +API_URL=https://localhost:3000 +AUTH_URL=https://local.auth.thx.network +AUTH_URL_TEST_OVERRIDE=http://localhost:3030 +PUBLIC_URL=https://localhost:8081 +DASHBOARD_URL=https://localhost:8082 +WALLET_URL=https://localhost:8083 +WIDGETS_URL=https://localhost:8085 + +# AWS +AWS_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxxxxxxxx" +AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +# OAuth +SECURE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +INITIAL_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +AUTH_CLIENT_ID=P4bLsMzQIwWlr_DOhH_qE +AUTH_CLIENT_SECRET=o7RMGeLieULzp0bAiuq6nfukFBCLKxo8N867SzeZNrinshSb15W9NggU93CdtrE_0jTWLUl3U2YkcXYtD3hdcQ + +# Others +PORT=3030 +GTM=xxxxxxxxx +CWD="/usr/src/app/" +JWKS_JSON={"keys":[{"e":"AQAB","n":"oGGYqftZswJRU_vfKDi7jFNgH20LOVfOgSYBu39GEvYWeygBTGuHmf2Ict7m70IKiFAY24kOhMnNjFJ6B0dxdBt5Hf3Kw-VoTKp1MDuy6_wao51WbR5G6II7ND3_2jucyNLeHpYwKHmk00yo4XTBlihNB_ogLCsblupHFceULfq5DN1whTGp-ZHTdYZaSZGXBC_v046vzqIqdKF785AzN56fBDlWhJ0CB9PdLSqj5hd91mxIbJqarHHWR844R5gOLE5ZMJMwX7SUgirFXhDo_VPVKJsP06SxGWBHe8HaYYi6DSXA7i59yBWyJy80F4t3OBTX-AWkzSfJ-_O9zPEBXQ","d":"Y3BfsXTwhrb3KfVOxad3UWgYfyOjA-jXVufzxwcAsZz3D2EcfP9m0imKQn7F_K6bzSysXOG7qMVetpQkqQK4615lWB0VbSR96Jr_kepR41MqSSuyfHF7UYn9n0SZr_uqGDH2GkzQiyfw9Dnhs_gqVymhqUKCVCBeONIFRRG7ZE3Gythc9WxSpahmX4hvSBSzU_DkfHyitPpCAqitJBEZCp5Ik7BmCYfXQ_5pozARKh4g1pj3cvyZ1FS7t_6wjyG3yP1DEkFb0Di3tscIYxL2EK3lTo2IDL-J53bT9dL_Vs3FsXi1iU_lzn8P7wmMi0auCoL15FJYfT3kZOXfBo8ZWQ","p":"zK6sr6VUTsqwqD-xULZ2SP6h7JC6yvLsNoRp8-r4sdkyj4CI0KHd9iBDQHgpuEMkwM7YpWD1eHPK3R-fnz8AAo2QsP_uaAEEIt7nzBmzLBWOzXcrFEOrtWQ4ojwF9y3nWfdbWZc7CH3uJ1cF7TnzaqO9zChT9jNFhY0o1ECuKis","q":"yJeAegY_6kjhRPc3XWnnYfn8fUYFZ9P7Dylq-seZGT3q6anP1h_yVlXh5h5rYMe7nmTLeyc9RuhPLJY0hDTYKZd5-wCLsfGrEGEa_FQITQElmUYkBjuqqxjoHlVmgCopGjvg9uUCm_hV2HfS6scLWJbnd9NSSvKza_euMly7Zpc","dp":"hiIiQKkT6t6hjmDPDpnEQmm8K49dGgrACaoU1Sgy-jngDHKrNi4di2HxMJqOnJZDy6bCCv7TXrBjTS2gKXfbxdCH9baCwd2InGF_fh2JcWZfQv7JWGpQaHrZMlgrgKSwbCDR2JBmu1XrcBRzadcEUeokQ1paS4mmEbNEAnSwrik","dq":"YpMBEfYsRqfV_Bw42vEGQgGlcLKOSX3ErKi_58lalSr2XCmU9zbv0jmWL43XWtIMg0QpMrYPyN60ucZ-vFFzwMytpwmXnLSUShJOPp3VDnJ31aDAZ0e_ESHGP9Hb8zPEyx2N6gaUh608Eoqf3bw--SP_T8VLdYVbYr1un1UuyrU","qi":"bm0rMfUEAfDXvsb2hUNesL55cWU5soCCn9A2xYyKyRRVIQO3dIp-iPxI0pv0U0JPl0sqQ8fa8A1xmdOvkL71pQDzBco4mAfA1IE38srnWKhbM9XMsJChwB6jCJAtsqHBwTgbqwZNDouVr9ZE2CbP7DVG66FUkqV8NXNb9zXEUv4","kty":"RSA","kid":"qSjSTujaClXGGkwW4zdC1zpRHxAq099krc8TTFfXYlg","alg":"RS256","use":"sig"}]} + +GOOGLE_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxx +GOOGLE_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +GOOGLE_REDIRECT_URI=https://localhost:3030/oidc/callback/youtube + +TWITTER_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxx +TWITTER_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +TWITTER_REDIRECT_URI=https://localhost:3030/oidc/callback/twitter + +GITHUB_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxx +GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +GITHUB_REDIRECT_URI=https://localhost:3030/oidc/callback/github + +LOCAL_CERT=../../../certs/localhost.crt +LOCAL_CERT_KEY=../../../certs/localhost.key \ No newline at end of file diff --git a/apps/auth/.eslintrc.json b/apps/auth/.eslintrc.json new file mode 100644 index 000000000..6156d669d --- /dev/null +++ b/apps/auth/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": { + "@typescript-eslint/no-explicit-any": "off" + } + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/auth/Dockerfile b/apps/auth/Dockerfile new file mode 100644 index 000000000..f25a91b05 --- /dev/null +++ b/apps/auth/Dockerfile @@ -0,0 +1,62 @@ +##################################################################################################### +## Develop stage +##################################################################################################### +FROM node:18-slim as develop + +WORKDIR /usr/src/app + +ENV NODE_OPTIONS="--max_old_space_size=4096" + +RUN apt-get update && apt-get install -y g++ make python3-pip build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev + +COPY package.json yarn.lock ./ +RUN yarn +COPY . . + +CMD [ "npx", "nx", "serve", "auth" ] + +##################################################################################################### +## Build stage +##################################################################################################### +FROM node:18-slim as build + +WORKDIR /usr/src/app +COPY --from=develop ./usr/src/app/ ./ +RUN npx nx build auth --prod +COPY ./newrelic.js ./yarn.lock ./dist/apps/auth/ + + +##################################################################################################### +## Production stage +##################################################################################################### +FROM node:18-slim as production + +ENV NODE_ENV=production + +WORKDIR /usr/src/app +COPY --from=build ./usr/src/app/dist/apps/auth/package.json ./usr/src/app/dist/apps/auth/yarn.lock ./ + +# Install dependencies and packages +RUN apt-get update && apt-get install -y \ + g++ \ + make \ + python3-pip \ + build-essential \ + libcairo2-dev \ + libpango1.0-dev \ + libjpeg-dev \ + libgif-dev \ + librsvg2-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install your application dependencies (assuming it uses Node.js) +RUN yarn + +# Clean up unnecessary packages and files +RUN apt-get purge -y --auto-remove build-essential && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=build ./usr/src/app/dist/apps/auth ./ + +CMD [ "main.js" ] \ No newline at end of file diff --git a/apps/auth/jest.config.ts b/apps/auth/jest.config.ts new file mode 100644 index 000000000..9dcc1b44c --- /dev/null +++ b/apps/auth/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'auth', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/auth', +}; diff --git a/apps/auth/package.json b/apps/auth/package.json new file mode 100644 index 000000000..20b8ea4fa --- /dev/null +++ b/apps/auth/package.json @@ -0,0 +1,16 @@ +{ + "name": "@thxnetwork/auth", + "contributors": [ + "Peter Polman <peter@thx.network>", + "GarfDev <garfdev.13@gmail.com>", + "Valeria Grazzini <vgrazzini@gmail.com>", + "Bram Rongen <mail@bramrongen.nl>", + "Justina Mary <justinamary27@gmail.com>" + ], + "license": "AGPL-3.0", + "version": "1.41.175", + "scripts": { + "migrate": "node migrate-mongo.js up", + "migrate:down": "node migrate-mongo.js down" + } +} diff --git a/apps/auth/project.json b/apps/auth/project.json new file mode 100644 index 000000000..94c6a61fc --- /dev/null +++ b/apps/auth/project.json @@ -0,0 +1,99 @@ +{ + "name": "auth", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/auth/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "@nx/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "target": "node", + "compiler": "tsc", + "outputPath": "dist/apps/auth", + "main": "apps/auth/src/main.ts", + "tsConfig": "apps/auth/tsconfig.app.json", + "assets": ["apps/auth/src/assets", "apps/auth/src/app/migrations"], + "webpackConfig": "apps/api/webpack.config.js", + "generatePackageJson": true, + "additionalEntryPoints": [ + { + "entryPath": "apps/auth/scripts/migrate-mongo.ts", + "entryName": "migrate-mongo" + }, + { + "entryPath": "apps/auth/scripts/script.ts", + "entryName": "script" + } + ] + }, + "configurations": { + "development": {}, + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false + } + } + }, + "serve": { + "executor": "@nx/js:node", + "options": { + "buildTarget": "auth:build", + "host": "localhost", + "port": 3030, + "inspect": false, + "watch": true + }, + "configurations": { + "production": { + "buildTarget": "auth:build:production" + } + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/auth/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/auth/jest.config.ts", + "testTimeout": 60000, + "passWithNoTests": false, + "bail": false, + "runInBand": true, + "logHeapUsage": true + } + }, + "script": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "node script.js", + "cwd": "dist/apps/auth" + } + }, + "migrate-db": { + "dependsOn": ["^build"], + "executor": "nx:run-commands", + "options": { + "command": "node migrate-mongo.js up", + "cwd": "dist/apps/auth" + } + }, + "migrate-db-create": { + "executor": "nx:run-commands", + "options": { + "command": "migrate-mongo create -f src/app/config/migrate-mongo-create-only.json", + "cwd": "apps/auth" + } + } + } +} diff --git a/apps/auth/scripts/migrate-mongo.ts b/apps/auth/scripts/migrate-mongo.ts new file mode 100644 index 000000000..46bdae37b --- /dev/null +++ b/apps/auth/scripts/migrate-mongo.ts @@ -0,0 +1,4 @@ +import { migrateMongoScript } from '@thxnetwork/common/migrate-mongo'; +import migrateMongoConfig from '../src/app/config/migrate-mongo'; + +migrateMongoScript(migrateMongoConfig); diff --git a/apps/auth/scripts/script.ts b/apps/auth/scripts/script.ts new file mode 100644 index 000000000..c5fdb64b2 --- /dev/null +++ b/apps/auth/scripts/script.ts @@ -0,0 +1,14 @@ +import db from '@thxnetwork/auth/util/database'; + +db.connect(process.env.MONGODB_URI_PROD); + +async function main() { + // +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/apps/auth/src/app/app.ts b/apps/auth/src/app/app.ts new file mode 100644 index 000000000..dac08c91b --- /dev/null +++ b/apps/auth/src/app/app.ts @@ -0,0 +1,58 @@ +import 'express-async-errors'; + +import axios from 'axios'; +import axiosBetterStacktrace from 'axios-better-stacktrace'; +import compression from 'compression'; +import express from 'express'; +import expressEJSLayouts from 'express-ejs-layouts'; +import path from 'path'; +import db from './util/database'; +import morgan from './middlewares/morgan'; +import morganBody from 'morgan-body'; +import { xssProtection } from 'lusca'; +import { DASHBOARD_URL, GTM, MONGODB_URI, NODE_ENV, PORT, PUBLIC_URL, WIDGET_URL } from './config/secrets'; +import RouterRoot from './controllers'; +import { corsHandler, errorLogger, errorNormalizer, errorOutput, notFoundHandler } from './middlewares'; +import { helmetInstance } from './util/helmet'; +import { assetsPath } from './util/path'; + +axiosBetterStacktrace(axios); + +const app = express(); + +db.connect(MONGODB_URI); + +app.set('port', PORT); +app.set('trust proxy', true); +app.set('layout', './layouts/default'); +app.set('view engine', 'ejs'); +app.set('views', path.join(assetsPath, 'views')); +app.use(compression()); +app.use(helmetInstance); +app.use(corsHandler); +app.use(morgan); + +morganBody(app, { + logRequestBody: NODE_ENV === 'development', + logResponseBody: false, // NODE_ENV === 'development', + skip: () => NODE_ENV === 'test', +}); + +app.use(expressEJSLayouts); +app.use(xssProtection(true)); +app.use(express.static(assetsPath)); +app.use('/', RouterRoot); +app.use(notFoundHandler); +app.use(errorLogger); +app.use(errorNormalizer); +app.use(errorOutput); + +app.locals = Object.assign(app.locals, { + gtm: GTM, + dashboardUrl: DASHBOARD_URL, + widgetUrl: WIDGET_URL, + publicUrl: PUBLIC_URL, + deployedAt: String(Date.now()), +}); + +export default app; diff --git a/apps/auth/src/app/config/migrate-mongo-create-only.json b/apps/auth/src/app/config/migrate-mongo-create-only.json new file mode 100644 index 000000000..f6776da66 --- /dev/null +++ b/apps/auth/src/app/config/migrate-mongo-create-only.json @@ -0,0 +1,4 @@ +{ + "migrationsDir": "src/app/migrations", + "moduleSystem": "commonjs" +} diff --git a/apps/auth/src/app/config/migrate-mongo.ts b/apps/auth/src/app/config/migrate-mongo.ts new file mode 100644 index 000000000..8ffe9b588 --- /dev/null +++ b/apps/auth/src/app/config/migrate-mongo.ts @@ -0,0 +1,13 @@ +import path from 'path'; +import { MONGODB_URI } from '@thxnetwork/auth/config/secrets'; + +export default { + migrationFileExtension: '.js', + mongodb: { + url: MONGODB_URI, + }, + migrationsDir: path.join(path.resolve(__dirname), 'app/migrations'), + changelogCollectionName: 'changelog', + useFileHash: false, + moduleSystem: 'commonjs', +}; diff --git a/apps/auth/src/app/config/oidc.ts b/apps/auth/src/app/config/oidc.ts new file mode 100644 index 000000000..cf28871b5 --- /dev/null +++ b/apps/auth/src/app/config/oidc.ts @@ -0,0 +1,221 @@ +import MongoAdapter from '../util/adapter'; +import { Account } from '../models/Account'; +import { AccountDocument } from '../models/Account'; +import { API_URL, INITIAL_ACCESS_TOKEN, NODE_ENV, SECURE_KEY } from '@thxnetwork/auth/config/secrets'; +import { Configuration, interactionPolicy } from 'oidc-provider'; +import { getJwks } from '../util/jwks'; + +const basePolicy = interactionPolicy.base(); +const promptAuth = new interactionPolicy.Prompt({ name: 'auth', requestable: true }); +const promptVerifyEmail = new interactionPolicy.Prompt({ name: 'verify_email', requestable: true }); +const promptConnect = new interactionPolicy.Prompt({ name: 'connect', requestable: true }); +const promptAccount = new interactionPolicy.Prompt({ name: 'account-settings', requestable: true }); +basePolicy.add(promptAuth); +basePolicy.add(promptVerifyEmail); +basePolicy.add(promptConnect); +basePolicy.add(promptAccount); + +// Consent prompt is a requirement for refresh_token grant +// so we only remove the checks and not the prompt +basePolicy.get('consent').checks.clear(); + +const keys = [SECURE_KEY.split(',')[0], SECURE_KEY.split(',')[1]]; +const config: Configuration = { + jwks: getJwks(), + adapter: MongoAdapter, + loadExistingGrant: async (ctx) => { + const grant = new ctx.oidc.provider.Grant({ + clientId: ctx.oidc.client.clientId, + accountId: ctx.oidc.session.accountId, + }); + + grant.addOIDCScope('openid offline_access'); + grant.addOIDCClaims(['sub', 'email']); + grant.addResourceScope(API_URL, ctx.oidc.client.scope); + await grant.save(); + return grant; + }, + async findAccount(ctx: any, sub: string) { + const account: AccountDocument = await Account.findById(sub); + + return { + accountId: sub, + claims: () => { + return { + sub, + ...account.toJSON(), + }; + }, + }; + }, + routes: { + authorization: '/authorize', + }, + extraParams: [ + 'pool_id', + 'claim_id', + 'return_url', + 'signup_email', + 'signup_plan', + 'signup_offer', + 'verifyEmailToken', + 'prompt', + 'collaborator_request_token', + 'referral_code', + 'access_token_kind', + 'provider_scope', + 'auth_variant', + 'auth_signature', + 'auth_message', + 'auth_email', + ], + scopes: [ + 'openid', + 'offline_access', + 'account:read', + 'account:write', + 'accounts:read', + 'accounts:write', + 'brands:read', + 'brands:write', + 'pools:read', + 'pools:write', + 'rewards:read', + 'rewards:write', + 'members:read', + 'members:write', + 'memberships:read', + 'memberships:write', + 'withdrawals:read', + 'withdrawals:write', + 'deposits:read', + 'deposits:write', + 'erc20:read', + 'erc20:write', + 'erc721:read', + 'erc721:write', + 'erc1155:read', + 'erc1155:write', + 'promotions:read', + 'promotions:write', + 'point_balances:read', + 'point_balances:write', + 'point_rewards:read', + 'point_rewards:write', + 'transactions:read', + 'transactions:write', + 'payments:read', + 'payments:write', + 'widgets:write', + 'widgets:read', + 'relay:write', + 'metrics:read', + 'swaprule:read', + 'swaprule:write', + 'swap:read', + 'swap:write', + 'claims:write', + 'claims:read', + 'clients:write', + 'clients:read', + 'wallets:read', + 'wallets:write', + 'webhooks:read', + 'webhooks:write', + 'web3_quests:read', + 'web3_quests:write', + 'custom_rewards:read', + 'custom_rewards:write', + 'coupon_rewards:read', + 'coupon_rewards:write', + 'discord_role_rewards:read', + 'discord_role_rewards:write', + 'erc20_rewards:read', + 'erc20_rewards:write', + 'erc721_rewards:read', + 'erc721_rewards:write', + 'referral_rewards:read', + 'referral_rewards:write', + 'referal_reward_claims:read', + 'referal_reward_claims:write', + 'pool_analytics:read', + 'pool_subscription:read', + 'pool_subscription:write', + 'merchants:write', + 'merchants:read', + 'identities:write', + 'identities:read', + 'events:write', + 'events:read', + ], + claims: { + openid: ['sub', 'email', 'variant', 'address'], + }, + ttl: { + Interaction: 24 * 60 * 60, // 24 hours in seconds + Session: 24 * 60 * 60, // 24 hours in seconds + Grant: 24 * 60 * 60, // 24 hours in seconds + IdToken: 24 * 60 * 60, // 24 hours in seconds + RefreshToken: 24 * 60 * 60, // 24 hours in seconds + AccessToken: 24 * 60 * 60, // 24 hours in seconds, + AuthorizationCode: 10 * 60, // 10 minutes in seconds + ClientCredentials: 1 * 60 * 60, // 10 minutes in seconds + }, + interactions: { + policy: basePolicy, + url(ctx: any, interaction: any) { + return `/oidc/${interaction.uid}`; + }, + }, + features: { + userinfo: { enabled: false }, + devInteractions: { enabled: false }, + clientCredentials: { enabled: true }, + encryption: { enabled: true }, + introspection: { enabled: true }, + registration: { enabled: true, initialAccessToken: INITIAL_ACCESS_TOKEN }, + registrationManagement: { enabled: true }, + resourceIndicators: { + enabled: true, + defaultResource: () => API_URL, + getResourceServerInfo: async (ctx, resourceIndicator, client) => { + return { + scope: client.scope, + audience: client.clientId, + accessTokenTTL: 1 * 60 * 60, + accessTokenFormat: 'jwt', + }; + }, + useGrantedResource: () => true, + }, + rpInitiatedLogout: { + enabled: true, + logoutSource: async (ctx: any, form: any) => { + ctx.body = `<!DOCTYPE html> + <head> + <title>Logout</title> + </head> + <body> + ${form} + <script src="/js/logout.js"></script> + </body> + </html>`; + }, + }, + }, + cookies: { + long: { signed: true, secure: true, sameSite: 'none' }, + short: { signed: true, secure: true, sameSite: 'none' }, + keys, + }, +}; + +if (NODE_ENV === 'test') { + config.pkce = { + methods: ['S256'], + required: () => false, + }; + config.cookies.long = undefined; + config.cookies.short = undefined; +} +export default config; diff --git a/apps/auth/src/app/config/secrets.ts b/apps/auth/src/app/config/secrets.ts new file mode 100644 index 000000000..509f586ec --- /dev/null +++ b/apps/auth/src/app/config/secrets.ts @@ -0,0 +1,80 @@ +import path from 'path'; + +const required = [ + 'API_URL', + 'AUTH_URL', + 'WALLET_URL', + 'PUBLIC_URL', + 'DASHBOARD_URL', + 'MONGODB_URI', + 'PORT', + 'SECURE_KEY', + 'AWS_ACCESS_KEY_ID', + 'AWS_SECRET_ACCESS_KEY', +]; + +// For production (docker containers) we should require JWKS_JSON to be set since otherwise each container +// would generate their own jwks.json. +if (process.env.NODE_ENV === 'production') { + required.push('JWKS_JSON'); +} + +required.forEach((value: string) => { + if (!process.env[value]) { + console.log(`Set ${value} environment variable.`); + process.exit(1); + } +}); + +// This allows you to use a single .env file with both regular and test configuration. This allows for an +// easy to use setup locally without having hardcoded credentials during test runs. +if (process.env.NODE_ENV === 'test') { + if (process.env.AUTH_URL_TEST_OVERRIDE !== undefined) process.env.AUTH_URL = process.env.AUTH_URL_TEST_OVERRIDE; + if (process.env.PORT_TEST_OVERRIDE !== undefined) process.env.AUTH_PORT = process.env.PORT_TEST_OVERRIDE; + if (process.env.MONGODB_URI_TEST_OVERRIDE !== undefined) + process.env.MONGODB_URI = process.env.MONGODB_URI_TEST_OVERRIDE; +} + +export const VERSION = 'v1'; +export const GITHUB_API_ENDPOINT = 'https://api.github.com'; +export const TWITTER_API_ENDPOINT = 'https://api.twitter.com/2'; +export const GOOGLE_API_ENDPOINT = 'https://www.googleapis.com'; +export const DISCORD_API_ENDPOINT = 'https://discord.com/api/v10'; +export const TWITCH_API_ENDPOINT = 'https://api.twitch.tv/helix'; + +export const CWD = process.env.CWD || path.resolve(__dirname, '../../../apps/auth/src'); + +export const NODE_ENV = process.env.NODE_ENV; +export const AUTH_URL = process.env.AUTH_URL; +export const API_URL = process.env.API_URL; +export const WALLET_URL = process.env.WALLET_URL; +export const PUBLIC_URL = process.env.PUBLIC_URL; +export const DASHBOARD_URL = process.env.DASHBOARD_URL; +export const WIDGET_URL = process.env.WIDGET_URL; +export const MONGODB_URI = String(process.env.MONGODB_URI); +export const PORT = process.env.PORT; +export const SECURE_KEY = process.env.SECURE_KEY; +export const GTM = process.env.GTM; +export const INITIAL_ACCESS_TOKEN = process.env.INITIAL_ACCESS_TOKEN; +export const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID; +export const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET; +export const TWITTER_CLIENT_ID = process.env.TWITTER_CLIENT_ID; +export const TWITTER_CLIENT_SECRET = process.env.TWITTER_CLIENT_SECRET; +export const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID; +export const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET; +export const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID; +export const DISCORD_CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET; +export const TWITCH_CLIENT_ID = process.env.TWITCH_CLIENT_ID; +export const TWITCH_CLIENT_SECRET = process.env.TWITCH_CLIENT_SECRET; +export const AUTH_CLIENT_SECRET = process.env.AUTH_CLIENT_SECRET; +export const AUTH_CLIENT_ID = process.env.AUTH_CLIENT_ID; +export const JWKS_JSON = process.env.JWKS_JSON; +export const LOCAL_CERT = process.env.LOCAL_CERT; +export const LOCAL_CERT_KEY = process.env.LOCAL_CERT_KEY; +export const MIXPANEL_TOKEN = process.env.MIXPANEL_TOKEN; +export const CYPRESS_EMAIL = process.env.CYPRESS_EMAIL || 'cypress@thx.network'; +export const HUBSPOT_ACCESS_TOKEN = process.env.HUBSPOT_ACCESS_TOKEN; +export const BOT_TOKEN = process.env.BOT_TOKEN; +export const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID; +export const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY; +export const COOKIE_DOMAIN = NODE_ENV === 'production' ? '.thx.network' : undefined; diff --git a/apps/auth/src/app/controllers/account/account.router.ts b/apps/auth/src/app/controllers/account/account.router.ts new file mode 100644 index 000000000..0329349ac --- /dev/null +++ b/apps/auth/src/app/controllers/account/account.router.ts @@ -0,0 +1,44 @@ +import express from 'express'; +import { + getAccount, + getAccountByAddress, + getAccountByEmail, + getAccountByDiscord, + getAccountByIdentity, +} from './get.controller'; +import Patch from './patch.controller'; +import Delete from './delete.controller'; +import List from './list.controller'; +import TokenRead from './tokens/get.controller'; +import TokenRemove from './tokens/delete.controller'; +import { validate } from '../../util/validate'; +import { guard, validateJwt } from '../../middlewares'; + +const router = express.Router({ mergeParams: true }); + +router.use(validateJwt); +router.get('/discord/:discordId', guard.check(['accounts:read']), validate([]), getAccountByDiscord); +router.get('/address/:address', guard.check(['accounts:read']), validate([]), getAccountByAddress); +router.get('/email/:email', guard.check(['accounts:read']), validate([]), getAccountByEmail); +router.get('/identity/:identity', guard.check(['accounts:read']), validate([]), getAccountByIdentity); + +router.get('/:sub', guard.check(['accounts:read']), getAccount); +router.patch('/:sub', guard.check(['accounts:read', 'accounts:write']), validate(Patch.validation), Patch.controller); +router.delete('/:sub', guard.check(['accounts:write']), validate(Delete.validation), Delete.controller); +router.post('/', guard.check(['accounts:read']), validate(List.validation), List.controller); + +router.get( + '/:sub/tokens/:kind', + guard.check(['accounts:read', 'accounts:write']), + validate(TokenRead.validation), + TokenRead.controller, +); + +router.delete( + '/:sub/tokens/:kind', + guard.check(['accounts:read', 'accounts:write']), + validate(TokenRemove.validation), + TokenRemove.controller, +); + +export default router; diff --git a/apps/auth/src/app/controllers/account/account.test.ts b/apps/auth/src/app/controllers/account/account.test.ts new file mode 100644 index 000000000..9a30a7279 --- /dev/null +++ b/apps/auth/src/app/controllers/account/account.test.ts @@ -0,0 +1,121 @@ +import request from 'supertest'; +import app from '../../app'; +import db from '../../util/database'; +import { accountAddress, accountEmail, jwksResponse } from '../../util/jest'; +import { INITIAL_ACCESS_TOKEN } from '@thxnetwork/auth/config/secrets'; +import { AccountVariant, AccountPlanType } from '@thxnetwork/common/enums'; +import { mockAuthPath } from '@thxnetwork/auth/util/jest/mock'; +import AuthService from '@thxnetwork/auth/services/AuthService'; + +const http = request.agent(app); + +describe('Account Controller', () => { + let authHeader: string, basicAuthHeader: string, sub: string; + + beforeAll(async () => { + await db.truncate(); + + // Mock jwks endpoint as jwks-rsa getKeyInterceptor does not work with supertest. + await mockAuthPath('get', '/jwks', 200, jwksResponse); + + async function requestToken() { + const res = await http + .post('/token') + .set({ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': basicAuthHeader, + }) + .send({ + grant_type: 'client_credentials', + scope: 'openid accounts:read accounts:write', + }); + return `Bearer ${res.body.access_token}`; + } + + async function registerClient() { + const res = await http + .post('/reg') + .set({ Authorization: `Bearer ${INITIAL_ACCESS_TOKEN}` }) + .send({ + application_type: 'web', + client_name: 'THX API', + grant_types: ['client_credentials'], + redirect_uris: [], + response_types: [], + scope: 'openid accounts:read accounts:write', + }); + + return 'Basic ' + Buffer.from(`${res.body.client_id}:${res.body.client_secret}`).toString('base64'); + } + + basicAuthHeader = await registerClient(); + authHeader = await requestToken(); + + const account = await AuthService.signup({ + plan: AccountPlanType.Lite, + email: accountEmail, + variant: AccountVariant.EmailPassword, + active: true, + }); + sub = String(account._id); + }); + + afterAll(async () => { + await db.disconnect(); + }); + + describe('GET /account/:id', () => { + it('HTTP 200', async () => { + const res = await http + .get(`/accounts/${sub}`) + .set({ + Authorization: authHeader, + }) + .send(); + expect(res.status).toBe(200); + expect(res.body.email).toBe(accountEmail); + expect(res.body.variant).toBe(AccountVariant.EmailPassword); + }); + }); + + describe('GET /accounts', () => { + it('HTTP 200', async () => { + const res = await http + .post(`/accounts`) + .send({ + subs: JSON.stringify([sub]), + }) + .set({ + Authorization: authHeader, + }); + expect(res.status).toBe(200); + expect(res.body.length).toBe(1); + expect(res.body[0].email).toBe(accountEmail); + }); + }); + + describe('PATCH /accounts/:id', () => { + it('HTTP 200', async () => { + const res = await http + .patch(`/accounts/${sub}`) + .set({ + Authorization: authHeader, + }) + .send({ + address: accountAddress, + }); + expect(res.status).toBe(200); + }); + + it('HTTP 200', async () => { + const res = await http + .get(`/accounts/${sub}`) + .set({ + Authorization: authHeader, + }) + .send(); + expect(res.status).toBe(200); + expect(res.body.address).toBe(accountAddress); + }); + }); +}); diff --git a/apps/auth/src/app/controllers/account/delete.controller.ts b/apps/auth/src/app/controllers/account/delete.controller.ts new file mode 100644 index 000000000..c654ede0b --- /dev/null +++ b/apps/auth/src/app/controllers/account/delete.controller.ts @@ -0,0 +1,12 @@ +import { Request, Response } from 'express'; +import { AccountService } from '../../services/AccountService'; +import { param } from 'express-validator'; + +const validation = [param('sub').isMongoId()]; + +const controller = async (req: Request, res: Response) => { + await AccountService.remove(req.auth.sub); + res.status(204).end(); +}; + +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/account/get.controller.ts b/apps/auth/src/app/controllers/account/get.controller.ts new file mode 100644 index 000000000..77af77d0c --- /dev/null +++ b/apps/auth/src/app/controllers/account/get.controller.ts @@ -0,0 +1,85 @@ +import { Request, Response } from 'express'; +import { NotFoundError } from '../../util/errors'; +import { AccountService } from '../../services/AccountService'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import { AccountDocument } from '@thxnetwork/auth/models/Account'; +import TokenService from '@thxnetwork/auth/services/TokenService'; + +async function decorate(account: AccountDocument) { + const sub = String(account._id); + const kinds = [ + AccessTokenKind.Google, + AccessTokenKind.Twitter, + AccessTokenKind.Discord, + AccessTokenKind.Twitch, + AccessTokenKind.Github, + ]; + const tokens = (await Promise.all(kinds.map((kind) => TokenService.getToken(account, kind)))) + .filter((token) => !!token) + .map(({ kind, scopes, userId, metadata }) => ({ kind, scopes, userId, metadata })); + const profileImg = account.profileImg || `https://api.dicebear.com/7.x/identicon/svg?seed=${sub}`; + + return { + sub, + profileImg, + username: account.username, + address: account.address, + firstName: account.firstName, + lastName: account.lastName, + website: account.website, + organisation: account.organisation, + isEmailVerified: account.isEmailVerified, + plan: account.plan, + email: account.email, + variant: account.variant, + role: account.role, + goal: account.goal, + identity: account.identity, + tokens, + }; +} + +export const getMe = async (req: Request, res: Response) => { + const account = await AccountService.get(req.auth.sub); + if (!account) throw new NotFoundError('Could not find the account for this sub'); + + res.send(await decorate(account)); +}; + +export const getAccount = async (req: Request, res: Response) => { + const account = await AccountService.get(req.params.sub); + if (!account) throw new NotFoundError('Could not find the account for this sub'); + + res.send(await decorate(account)); +}; + +export const getAccountByAddress = async (req: Request, res: Response) => { + const account = await AccountService.getByAddress(req.params.address); + if (!account) return res.end(); + + res.send(await decorate(account)); +}; + +export const getAccountByEmail = async (req: Request, res: Response) => { + const account = await AccountService.getByEmail(req.params.email); + if (!account) return res.end(); + + res.send(await decorate(account)); +}; + +export const getAccountByIdentity = async (req: Request, res: Response) => { + const account = await AccountService.getByIdentity(req.params.identity); + if (!account) return res.end(); + + res.send(await decorate(account)); +}; + +export const getAccountByDiscord = async (req: Request, res: Response) => { + const token = await TokenService.findTokenForUserId(req.params.discordId, AccessTokenKind.Discord); + if (!token) return res.end(); + + const account = await AccountService.get(token.sub); + if (!account) return res.end(); + + res.send(await decorate(account)); +}; diff --git a/apps/auth/src/app/controllers/account/list.controller.ts b/apps/auth/src/app/controllers/account/list.controller.ts new file mode 100644 index 000000000..be68e2fb6 --- /dev/null +++ b/apps/auth/src/app/controllers/account/list.controller.ts @@ -0,0 +1,62 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { Token } from '@thxnetwork/auth/models/Token'; +import { AccountService } from '@thxnetwork/auth/services/AccountService'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; + +const validation = [ + body('subs') + .optional() + .custom((subs) => { + return Array.isArray(JSON.parse(subs)); + }) + .customSanitizer((subs) => subs && JSON.parse(subs)), + body('query').optional().isString(), +]; + +const controller = async (req: Request, res: Response) => { + let accounts = []; + const { subs, query } = req.body; + if (subs && subs.length) { + accounts = await AccountService.find({ _id: req.body.subs }); + } + if (query) { + accounts = await AccountService.findByQuery({ query: req.body.query }); + } + + const result = await Promise.all( + accounts.map(async (account) => { + const sub = String(account._id); + const kinds = [ + AccessTokenKind.Google, + AccessTokenKind.Twitter, + AccessTokenKind.Discord, + AccessTokenKind.Twitch, + AccessTokenKind.Github, + ]; + const tokens = await Token.find({ sub, kind: { $in: kinds } }); + const profileImg = account.profileImg || `https://api.dicebear.com/7.x/identicon/svg?seed=${sub}`; + + return { + sub, + profileImg, + username: account.username, + address: account.address, + firstName: account.firstName, + lastName: account.lastName, + website: account.website, + organisation: account.organisation, + plan: account.plan, + email: account.email, + variant: account.variant, + role: account.role, + goal: account.goal, + tokens: tokens.map(({ kind, sub, userId, metadata }) => ({ kind, sub, userId, metadata })), + }; + }), + ); + + res.send(result); +}; + +export default { validation, controller }; diff --git a/apps/auth/src/app/controllers/account/patch.controller.ts b/apps/auth/src/app/controllers/account/patch.controller.ts new file mode 100644 index 000000000..d17d6c968 --- /dev/null +++ b/apps/auth/src/app/controllers/account/patch.controller.ts @@ -0,0 +1,23 @@ +import { Request, Response } from 'express'; +import { AccountService } from '../../services/AccountService'; +import { NotFoundError } from '../../util/errors'; +import { body, param } from 'express-validator'; + +const validation = [ + param('sub').isMongoId(), + body('email') + .optional() + .isEmail() + .customSanitizer((email) => email && email.toLowerCase()), +]; + +const controller = async (req: Request, res: Response) => { + let account = await AccountService.get(req.params.sub); + if (!account) throw new NotFoundError('Account not found.'); + + account = await AccountService.update(account, req.body); + + res.json(account); +}; + +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/account/tokens/delete.controller.ts b/apps/auth/src/app/controllers/account/tokens/delete.controller.ts new file mode 100644 index 000000000..271eb44c2 --- /dev/null +++ b/apps/auth/src/app/controllers/account/tokens/delete.controller.ts @@ -0,0 +1,15 @@ +import TokenService from '@thxnetwork/auth/services/TokenService'; +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Account } from '@thxnetwork/auth/models/Account'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; + +const validation = [param('sub').isMongoId(), param('kind').isString()]; + +export const controller = async (req: Request, res: Response) => { + const account = await Account.findById(req.params.sub); + await TokenService.unsetToken(account, req.params.kind as AccessTokenKind); + + res.status(204).end(); +}; +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/account/tokens/get.controller.ts b/apps/auth/src/app/controllers/account/tokens/get.controller.ts new file mode 100644 index 000000000..e81cb7c38 --- /dev/null +++ b/apps/auth/src/app/controllers/account/tokens/get.controller.ts @@ -0,0 +1,16 @@ +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { Account } from '@thxnetwork/auth/models/Account'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import TokenService from '@thxnetwork/auth/services/TokenService'; + +const validation = [param('sub').isMongoId(), param('kind').isString()]; + +export const controller = async (req: Request, res: Response) => { + const account = await Account.findById(req.params.sub); + const kind = req.params.kind as AccessTokenKind; + const token = await TokenService.getToken(account, kind); + + res.json(token); +}; +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/get.controller.ts b/apps/auth/src/app/controllers/get.controller.ts new file mode 100644 index 000000000..d731e0053 --- /dev/null +++ b/apps/auth/src/app/controllers/get.controller.ts @@ -0,0 +1,8 @@ +import { Request, Response } from 'express'; +import { WIDGET_URL } from '../config/secrets'; + +const controller = (_req: Request, res: Response) => { + res.redirect(WIDGET_URL); +}; + +export default { controller }; diff --git a/apps/auth/src/app/controllers/health/get.controller.ts b/apps/auth/src/app/controllers/health/get.controller.ts new file mode 100644 index 000000000..ac5da5f99 --- /dev/null +++ b/apps/auth/src/app/controllers/health/get.controller.ts @@ -0,0 +1,12 @@ +import { Response, Request } from 'express'; +import { name, version, license } from '../../../../package.json'; + +export const getHealth = (_req: Request, res: Response) => { + const jsonData = { + name, + version, + license, + }; + + res.header('Content-Type', 'application/json').send(JSON.stringify(jsonData, null, 4)); +}; diff --git a/apps/auth/src/app/controllers/health/health.router.ts b/apps/auth/src/app/controllers/health/health.router.ts new file mode 100644 index 000000000..f54c01ff6 --- /dev/null +++ b/apps/auth/src/app/controllers/health/health.router.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import { getHealth } from './get.controller'; + +const router = express.Router(); + +router.get('/', getHealth); + +export default router; diff --git a/apps/auth/src/app/controllers/index.ts b/apps/auth/src/app/controllers/index.ts new file mode 100644 index 000000000..12944a247 --- /dev/null +++ b/apps/auth/src/app/controllers/index.ts @@ -0,0 +1,23 @@ +import express, { json, urlencoded } from 'express'; +import rateLimit from 'express-rate-limit'; +import { oidc } from '../util/oidc'; +import Root from './get.controller'; +import RouterOIDC from './oidc/oidc.router'; +import RouterMe from './me/me.router'; +import RouterAccounts from './account/account.router'; +import RouterHealth from './health/health.router'; + +export const router = express.Router(); + +// Rate limit these public endpoints to max 10 req per minute +const rateLimiter = rateLimit({ windowMs: 60 * 1000, max: 10 }); + +// Rate limit these public endpoints to max 10 req per minute +router.get('/', rateLimiter, Root.controller); +router.use('/health', rateLimiter, json(), urlencoded({ extended: true }), RouterHealth); +router.use('/me', rateLimiter, json(), urlencoded({ extended: true }), RouterMe); +router.use('/oidc', rateLimiter, RouterOIDC); +router.use('/accounts', json(), urlencoded({ extended: true }), RouterAccounts); +router.use('/', oidc.callback()); + +export default router; diff --git a/apps/auth/src/app/controllers/me/get.controller.ts b/apps/auth/src/app/controllers/me/get.controller.ts new file mode 100644 index 000000000..889791578 --- /dev/null +++ b/apps/auth/src/app/controllers/me/get.controller.ts @@ -0,0 +1,8 @@ +import { Account } from '@thxnetwork/auth/models/Account'; + +const controller = async (req, res) => { + const account = await Account.findById(req.auth.sub); + res.json(account); +}; + +export default { controller }; diff --git a/apps/auth/src/app/controllers/me/me.router.ts b/apps/auth/src/app/controllers/me/me.router.ts new file mode 100644 index 000000000..6011c02b4 --- /dev/null +++ b/apps/auth/src/app/controllers/me/me.router.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { assertAuthorization, assertInteraction } from '@thxnetwork/auth/middlewares'; +import Read from './get.controller'; + +const router = express.Router(); + +router.get('/me', assertInteraction, assertAuthorization, Read.controller); + +export default router; diff --git a/apps/auth/src/app/controllers/oidc/account/email/get.ts b/apps/auth/src/app/controllers/oidc/account/email/get.ts new file mode 100644 index 000000000..e47ecd6b8 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/account/email/get.ts @@ -0,0 +1,18 @@ +import { Request, Response } from 'express'; +import AuthService from '@thxnetwork/auth/services/AuthService'; + +async function controller(req: Request, res: Response) { + const { uid, params } = req.interaction; + const { error, result } = await AuthService.verifyEmailToken(params.verifyEmailToken); + + return res.render('confirm', { + uid, + params, + alert: { + variant: error ? 'danger' : 'success', + message: error || result, + }, + }); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/account/get.ts b/apps/auth/src/app/controllers/oidc/account/get.ts new file mode 100644 index 000000000..51c244f36 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/account/get.ts @@ -0,0 +1,50 @@ +import { Request, Response } from 'express'; +import { AccountService } from '../../../services/AccountService'; +import { AccessTokenKind, AccountPlanType, OAuthRequiredScopes } from '@thxnetwork/common/enums'; +import TokenService from '@thxnetwork/auth/services/TokenService'; + +async function controller(req: Request, res: Response) { + const { uid, params, alert, session } = req.interaction; + const account = await AccountService.get(session.accountId); + + const kinds = [ + { kind: AccessTokenKind.Google, scopes: OAuthRequiredScopes.GoogleAuth }, + { kind: AccessTokenKind.Twitter, scopes: OAuthRequiredScopes.TwitterAuth }, + { kind: AccessTokenKind.Discord, scopes: OAuthRequiredScopes.DiscordAuth }, + { kind: AccessTokenKind.Twitch, scopes: OAuthRequiredScopes.TwitchAuth }, + { kind: AccessTokenKind.Github, scopes: OAuthRequiredScopes.GithubAuth }, + ]; + const [googleLoginUrl, twitterLoginUrl, discordLoginUrl, twitchLoginUrl, githubLoginUrl] = await Promise.all( + kinds.map(async ({ kind, scopes }) => { + const token = await TokenService.getToken(account, kind); + if (token) return; + return TokenService.getLoginURL({ kind, uid, scopes }); + }), + ); + + return res.render('account', { + uid, + alert, + params: { + ...params, + email: account.email, + isEmailVerified: account.isEmailVerified, + firstName: account.firstName, + lastName: account.lastName, + profileImg: account.profileImg, + organisation: account.organisation, + website: account.website, + address: account.address, + plan: account.plan, + planType: AccountPlanType[account.plan], + variant: account.variant, + googleLoginUrl, + twitterLoginUrl, + discordLoginUrl, + twitchLoginUrl, + githubLoginUrl, + }, + }); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/account/post.ts b/apps/auth/src/app/controllers/oidc/account/post.ts new file mode 100644 index 000000000..9a3877557 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/account/post.ts @@ -0,0 +1,64 @@ +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import { MailService } from '../../../services/MailService'; +import UploadProxy from '../../../proxies/UploadProxy'; +import { AccountService } from '../../../services/AccountService'; +import { ERROR_NO_ACCOUNT } from '../../../util/messages'; +import { createRandomToken } from '../../../util/tokens'; +import { AccessTokenKind } from '@thxnetwork/common/enums/AccessTokenKind'; +import { get24HoursExpiryTimestamp } from '@thxnetwork/auth/util/time'; +import { Account, AccountDocument } from '@thxnetwork/auth/models/Account'; +import TokenService from '@thxnetwork/auth/services/TokenService'; + +export const validation = [ + body('email').exists().isEmail(), + body('return_url').exists().isURL({ require_tld: false }), + body('firstName').optional().isString().isLength({ min: 0, max: 50 }), + body('lastName').optional().isString().isLength({ min: 0, max: 50 }), + body('organisation').optional().isString().isLength({ min: 0, max: 50 }), + body('website').optional().isURL({ require_tld: false }), + body().customSanitizer((val) => { + return { + email: val.email, + firstName: val.firstName, + lastName: val.lastName, + organisation: val.organisation, + website: val.website, + return_url: val.return_url, + }; + }), +]; + +export async function controller(req: Request, res: Response) { + const { uid, session } = req.interaction; + let account: AccountDocument = await AccountService.get(session.accountId); + if (!account) throw new Error(ERROR_NO_ACCOUNT); + + const file = (req.files as any)?.profile?.[0] as Express.Multer.File; + const isEmailChanged = req.body.email + ? account.email + ? account.email.toLowerCase() + : '' !== req.body.email + ? req.body.email.toLowerCase() + : '' + : false; + const profileImg = file ? await UploadProxy.post(file) : ''; + + account = await Account.findByIdAndUpdate(account._id, { ...req.body, profileImg }, { new: true }); + + if (isEmailChanged && account.email) { + account.isEmailVerified = false; + + await TokenService.setToken(account, { + kind: AccessTokenKind.VerifyEmail, + accessToken: createRandomToken(), + expiry: get24HoursExpiryTimestamp(), + }); + await account.save(); + await MailService.sendVerificationEmail(account, account.email, req.body.return_url); + } + + res.redirect(`/oidc/${uid}/account`); +} + +export default { validation, controller }; diff --git a/apps/auth/src/app/controllers/oidc/callback/callback.router.ts b/apps/auth/src/app/controllers/oidc/callback/callback.router.ts new file mode 100644 index 000000000..99cb81fc1 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/callback/callback.router.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import Read from './get.controller'; + +const router = express.Router({ mergeParams: true }); + +router.get('/:kind', Read.controller); + +export default router; diff --git a/apps/auth/src/app/controllers/oidc/callback/get.controller.ts b/apps/auth/src/app/controllers/oidc/callback/get.controller.ts new file mode 100644 index 000000000..f26f6a696 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/callback/get.controller.ts @@ -0,0 +1,18 @@ +import { Request, Response } from 'express'; +import { providerAccountVariantMap } from '@thxnetwork/common/maps'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import AuthService from '@thxnetwork/auth/services/AuthService'; +import TokenService from '@thxnetwork/auth/services/TokenService'; + +export async function controller(req: Request, res: Response) { + const { code, interaction } = await AuthService.redirectCallback(req); + const kind = req.params.kind as AccessTokenKind; + const token = await TokenService.request({ kind, code }); + const variant = providerAccountVariantMap[kind]; + const account = await AuthService.connect(interaction, token, variant); + const returnUrl = await AuthService.getReturn(interaction, account); + + res.redirect(returnUrl); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/connect/get.ts b/apps/auth/src/app/controllers/oidc/connect/get.ts new file mode 100644 index 000000000..baedf0e2b --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/connect/get.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { oidc } from '../../../util/oidc'; +import TokenService from '../../../services/TokenService'; + +async function controller(req: Request, res: Response) { + const { uid, params } = req.interaction; + + // If no provider scopes are requested redirect to the return url directly + // as there is nothing to connect + if (!params.provider_scope) { + await oidc.interactionResult(req, res, {}, { mergeWithLastSubmission: true }); + return res.redirect(params.return_url); + } + + const scopes = params.provider_scope.split(' '); + const loginURL = TokenService.getLoginURL({ uid, kind: params.access_token_kind, scopes }); + + res.redirect(loginURL); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/disconnect/post.controller.ts b/apps/auth/src/app/controllers/oidc/disconnect/post.controller.ts new file mode 100644 index 000000000..2bad1e664 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/disconnect/post.controller.ts @@ -0,0 +1,16 @@ +import TokenService from '@thxnetwork/auth/services/TokenService'; +import { Request, Response } from 'express'; +import { param } from 'express-validator'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import { AccountService } from '@thxnetwork/auth/services/AccountService'; + +const validation = [param('sub').isMongoId(), param('kind').isString()]; + +export const controller = async (req: Request, res: Response) => { + const { session } = req.interaction; + const account = await AccountService.get(session.accountId); + await TokenService.unsetToken(account, req.params.kind as AccessTokenKind); + + res.redirect(`/oidc/${req.params.uid}/account`); +}; +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/oidc/get.controller.ts b/apps/auth/src/app/controllers/oidc/get.controller.ts new file mode 100644 index 000000000..c3c007e43 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/get.controller.ts @@ -0,0 +1,42 @@ +import { Request, Response } from 'express'; +import { oidc } from '@thxnetwork/auth/util/oidc'; +import { AccountVariant } from '@thxnetwork/common/enums'; +import AuthService from '@thxnetwork/auth/services/AuthService'; + +async function controller(req: Request, res: Response) { + const { uid, prompt, params } = await oidc.interactionDetails(req, res); + + // If params.auth_variant is available, deeplink to auth_variant + if (params && params.auth_variant) { + const variant = Number(params.auth_variant) as AccountVariant; + const authVariantRedirectMap = { + [AccountVariant.EmailPassword]: () => + AuthService.redirectOTP(req, res, { email: String(params.auth_email) }), + [AccountVariant.Metamask]: () => + AuthService.redirectWalletConnect(req, res, { + message: params.auth_message as string, + signature: params.auth_signature as string, + }), + [AccountVariant.SSOGoogle]: () => AuthService.redirectSSO(req, res, { uid, variant }), + [AccountVariant.SSODiscord]: () => AuthService.redirectSSO(req, res, { uid, variant }), + [AccountVariant.SSOTwitter]: () => AuthService.redirectSSO(req, res, { uid, variant }), + [AccountVariant.SSOTwitch]: () => AuthService.redirectSSO(req, res, { uid, variant }), + [AccountVariant.SSOGithub]: () => AuthService.redirectSSO(req, res, { uid, variant }), + }; + return authVariantRedirectMap[variant](); + } + + // For other cases check the prompt or params.prompt values + const redirectMap = { + 'verify_email': `/oidc/${uid}/account/email/verify`, + 'account-settings': `/oidc/${uid}/account`, + 'connect': `/oidc/${uid}/connect`, + 'login': `/oidc/${uid}/signin`, + }; + const paramsPrompt = params.prompt as string; + const url = redirectMap[paramsPrompt] ? redirectMap[paramsPrompt] : redirectMap[prompt.name]; + + res.redirect(url); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/oidc.router.ts b/apps/auth/src/app/controllers/oidc/oidc.router.ts new file mode 100644 index 000000000..972dce360 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/oidc.router.ts @@ -0,0 +1,41 @@ +import multer from 'multer'; +import express, { urlencoded } from 'express'; +import { assertInput, assertAuthorization, assertInteraction } from '../../middlewares'; + +import ReadConnect from './connect/get'; +import CreateDisconnect from './disconnect/post.controller'; +import ReadAccount from './account/get'; +import UpdateAccount from './account/post'; +import ReadAccountEmailVerify from './account/email/get'; +import ReadOIDC from './get.controller'; + +import RouterCallback from './callback/callback.router'; +import RouterSignin from './signin/signin.router'; + +const upload = multer(); +const router = express.Router(); + +router.use('/callback', RouterCallback); +router.use('/:uid/signin', RouterSignin); + +// Generic redirects from the OIDC router +router.get('/:uid', assertInteraction, ReadOIDC.controller); + +// Our custom connect flow for external accounts +router.get('/:uid/connect', assertInteraction, assertAuthorization, ReadConnect.controller); +router.post('/:uid/tokens/:kind/disconnect', assertInteraction, assertAuthorization, CreateDisconnect.controller); + +// @peterpolman Should deprecate and let dashboard use the /account in the API for patching account data +router.get('/:uid/account', assertInteraction, assertAuthorization, ReadAccount.controller); +router.get('/:uid/account/email/verify', assertInteraction, assertAuthorization, ReadAccountEmailVerify.controller); +router.post( + '/:uid/account', + urlencoded({ extended: false }), + upload.fields([{ name: 'profile', maxCount: 1 }]), + assertInteraction, + assertAuthorization, + assertInput(UpdateAccount.validation), + UpdateAccount.controller, +); + +export default router; diff --git a/apps/auth/src/app/controllers/oidc/signin/get.ts b/apps/auth/src/app/controllers/oidc/signin/get.ts new file mode 100644 index 000000000..89830742d --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/get.ts @@ -0,0 +1,98 @@ +import { Request, Response } from 'express'; +import { AUTH_URL, DASHBOARD_URL, WIDGET_URL } from '../../../config/secrets'; +import { AccountVariant, AccessTokenKind, OAuthRequiredScopes } from '@thxnetwork/common/enums'; +import ClaimProxy from '@thxnetwork/auth/proxies/ClaimProxy'; +import BrandProxy from '@thxnetwork/auth/proxies/BrandProxy'; +import PoolProxy from '@thxnetwork/auth/proxies/PoolProxy'; +import TokenService from '@thxnetwork/auth/services/TokenService'; +import EthereumService, { AUTH_REQUEST_TYPED_MESSAGE } from '@thxnetwork/auth/services/EthereumService'; + +async function controller(req: Request, res: Response) { + const { uid, params } = req.interaction; + const alert = {}; + const isWidget = params.return_url ? params.return_url.startsWith(WIDGET_URL) : false; + const isDashboard = params.return_url ? params.return_url.startsWith(DASHBOARD_URL) : false; + const isSignup = ['1', '2'].includes(params.signup_plan); + + let pool, + claim, + brand, + authenticationMethods = Object.values(AccountVariant); + + if (params.pool_id) { + brand = await BrandProxy.get(params.pool_id); + pool = await PoolProxy.getPool(params.pool_id); + if (pool.settings && pool.settings.authenticationMethods) { + authenticationMethods = pool.settings.authenticationMethods; + } + } + + if (pool && params.collaborator_request_token) { + alert['variant'] = 'success'; + alert[ + 'message' + ] = `<i class="fas fa-info-circle mr-2" aria-hidden="true"></i> Accept invite for <strong>${pool.settings.title}</strong>!`; + } + + if (params.pool_transfer_token) { + alert['variant'] = 'success'; + alert['message'] = `<i class="fas fa-gift mr-2" aria-hidden="true"></i>Sign in to access your campaign!`; + } + + if (params.claim_id) { + claim = await ClaimProxy.get(params.claim_id); + + alert['variant'] = 'success'; + if (claim.erc20) { + alert[ + 'message' + ] = `<i class="fas fa-gift mr-2" aria-hidden="true"></i>Sign in and claim your <strong>${claim.reward.amount} ${claim.erc20.symbol}!</strong>`; + } + if (claim.erc721) { + alert[ + 'message' + ] = `<i class="fas fa-gift mr-2" aria-hidden="true"></i>Sign in and claim your <strong>${claim.erc721.symbol} NFT!</strong>`; + } + } + + params.emailPasswordEnabled = authenticationMethods.includes(AccountVariant.EmailPassword); + params.metaMaskEnabled = authenticationMethods.includes(AccountVariant.Metamask); + params.trustedProviderAvailable = authenticationMethods.some((method: AccountVariant) => + [ + AccountVariant.SSOGoogle, + AccountVariant.SSOTwitter, + AccountVariant.SSOTwitch, + AccountVariant.SSOGithub, + AccountVariant.SSODiscord, + ].includes(method), + ); + + params.googleLoginUrl = authenticationMethods.includes(AccountVariant.SSOGoogle) + ? TokenService.getLoginURL({ + kind: AccessTokenKind.Google, + uid, + scopes: OAuthRequiredScopes.GoogleAuth, + }) + : null; + params.githubLoginUrl = authenticationMethods.includes(AccountVariant.SSOGithub) + ? TokenService.getLoginURL({ kind: AccessTokenKind.Github, uid, scopes: OAuthRequiredScopes.GithubAuth }) + : null; + params.discordLoginUrl = authenticationMethods.includes(AccountVariant.SSODiscord) + ? TokenService.getLoginURL({ kind: AccessTokenKind.Discord, uid, scopes: OAuthRequiredScopes.DiscordAuth }) + : null; + params.twitchLoginUrl = authenticationMethods.includes(AccountVariant.SSOTwitch) + ? TokenService.getLoginURL({ kind: AccessTokenKind.Twitch, uid, scopes: OAuthRequiredScopes.TwitchAuth }) + : null; + params.twitterLoginUrl = authenticationMethods.includes(AccountVariant.SSOTwitter) + ? TokenService.getLoginURL({ kind: AccessTokenKind.Twitter, uid, scopes: OAuthRequiredScopes.TwitterAuth }) + : null; + params.authRequestMessage = EthereumService.createTypedMessage(AUTH_REQUEST_TYPED_MESSAGE, AUTH_URL, uid); + + res.render('signin', { + uid, + params: { ...params, ...brand, claim, isWidget, isDashboard, isSignup }, + alert, + }); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/signin/grants.test.ts b/apps/auth/src/app/controllers/oidc/signin/grants.test.ts new file mode 100644 index 000000000..29074a643 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/grants.test.ts @@ -0,0 +1,150 @@ +import request from 'supertest'; +import app from '../../../app'; +import db from '../../../util/database'; +import { AccountService } from '../../../services/AccountService'; +import { INITIAL_ACCESS_TOKEN } from '../../../config/secrets'; +import { accountEmail, jwksResponse } from '../../../util/jest'; +import { AccountVariant, AccountPlanType } from '@thxnetwork/common/enums'; +import { mockAuthPath } from '@thxnetwork/auth/util/jest/mock'; + +const http = request.agent(app); + +describe('OAuth2 Grants', () => { + let authHeader: string, accessToken: string, sub: string; + + beforeAll(async () => { + await db.truncate(); + + // Mock jwks endpoint as jwks-rsa getKeyInterceptor does not work with supertest. + await mockAuthPath('get', '/jwks', 200, jwksResponse); + + const account = await AccountService.create({ + plan: AccountPlanType.Lite, + email: accountEmail, + variant: AccountVariant.EmailPassword, + active: true, + }); + sub = account.id; + }); + + afterAll(async () => { + await db.disconnect(); + }); + + describe('GET /.well-known/openid-configuration', () => { + it('HTTP 200', async () => { + const res = await http.get('/.well-known/openid-configuration'); + expect(res.status).toBe(200); + }); + }); + + describe('GET /accounts', () => { + it('HTTP 401 Unauthorized', async () => { + const res = await http.get('/accounts'); + expect(res.status).toBe(401); + }); + }); + + describe('GET /reg', () => { + it('HTTP 201', async () => { + const res = await http + .post('/reg') + .set({ Authorization: `Bearer ${INITIAL_ACCESS_TOKEN}` }) + .send({ + application_type: 'web', + client_name: 'THX API', + grant_types: ['client_credentials'], + redirect_uris: [], + response_types: [], + scope: 'openid accounts:read accounts:write', + }); + authHeader = 'Basic ' + Buffer.from(`${res.body.client_id}:${res.body.client_secret}`).toString('base64'); + + expect(res.status).toBe(201); + }); + }); + + describe('GET /token', () => { + it('HTTP 401 (invalid access token)', async () => { + const res = await http + .post('/token') + .set({ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'incorrect authorization code', + }) + .send({ + // resource: API_URL, + grant_type: 'client_credentials', + scope: 'openid accounts:read accounts:write', + }); + expect(res.status).toBe(400); + expect(res.body).toMatchObject({ + error: 'invalid_request', + error_description: 'invalid authorization header value format', + }); + }); + it('HTTP 401 (invalid grant)', async () => { + const res = await http + .post('/token') + .set({ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': authHeader, + }) + .send({ + grant_type: 'authorization_code', + scope: 'openid accounts:read accounts:write', + }); + expect(res.body).toMatchObject({ + error: 'unauthorized_client', + error_description: 'requested grant type is not allowed for this client', + }); + expect(res.status).toBe(400); + }); + + it('HTTP 401 (invalid scope)', async () => { + const res = await http + .post('/token') + .set({ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': authHeader, + }) + .send({ + grant_type: 'client_credentials', + scope: 'openid account:read account:write members:read members:write withdrawals:write asset_pools:read asset_pools:write rewards:read withdrawals:read deposits:read deposits:write', + }); + expect(res.body).toMatchObject({ + error: 'invalid_scope', + error_description: 'requested scope is not allowed', + scope: 'account:read', + }); + expect(res.status).toBe(400); + }); + + it('HTTP 200 (success)', async () => { + const res = await http + .post('/token') + .set({ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': authHeader, + }) + .send({ + grant_type: 'client_credentials', + scope: 'openid accounts:read accounts:write', + }); + accessToken = res.body.access_token; + + expect(res.status).toBe(200); + expect(accessToken).toBeDefined(); + }); + }); + + describe('GET /account/:id', () => { + it('HTTP 200', async () => { + const res = await http + .get(`/accounts/${sub}`) + .set({ Authorization: `Bearer ${accessToken}` }) + .send(); + expect(res.status).toBe(200); + }); + }); +}); diff --git a/apps/auth/src/app/controllers/oidc/signin/otp/get.ts b/apps/auth/src/app/controllers/oidc/signin/otp/get.ts new file mode 100644 index 000000000..1d13c0004 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/otp/get.ts @@ -0,0 +1,27 @@ +import { Request, Response } from 'express'; +import ClaimProxy from '@thxnetwork/auth/proxies/ClaimProxy'; +import BrandProxy from '@thxnetwork/auth/proxies/BrandProxy'; + +async function controller(req: Request, res: Response) { + const { jti, params } = req.interaction; + let claim, brand; + + if (params.claim_id) { + claim = await ClaimProxy.get(params.claim_id); + brand = await BrandProxy.get(claim.pool._id); + } + + if (params.pool_id) { + brand = await BrandProxy.get(params.pool_id); + } + + const alert = { + variant: 'info', + icon: 'question-circle', + message: `We sent a password to <strong>${params.email}</strong>`, + }; + + res.render('otp', { uid: jti, alert, email: req.interaction.email, params: { ...params, ...brand, claim } }); +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/signin/otp/post.ts b/apps/auth/src/app/controllers/oidc/signin/otp/post.ts new file mode 100644 index 000000000..ee5f1df0b --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/otp/post.ts @@ -0,0 +1,52 @@ +import BrandProxy from '@thxnetwork/auth/proxies/BrandProxy'; +import ClaimProxy from '@thxnetwork/auth/proxies/ClaimProxy'; +import { AccountService } from '@thxnetwork/auth/services/AccountService'; +import { oidc } from '@thxnetwork/auth/util/oidc'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import { Request, Response } from 'express'; +import { body } from 'express-validator'; +import TokenService from '@thxnetwork/auth/services/TokenService'; +import AuthService from '@thxnetwork/auth/services/AuthService'; + +const validation = [ + body('otp').exists().isString().isLength({ min: 5, max: 5 }), + body('returnUrl').exists().isURL({ require_tld: false }), +]; + +async function controller(req: Request, res: Response) { + let claim, brand; + const { uid, params } = req.interaction; + + const attemptCount = await AuthService.getOTPAttempt(req); + if (attemptCount >= 5) throw new Error('You have reached the maximum number of attempts.'); + + if (params.claim_id) { + claim = await ClaimProxy.get(params.claim_id); + brand = await BrandProxy.get(claim.pool._id); + } + + try { + const account = await AccountService.get(params.sub); + if (!account) throw new Error('No account could be found for this one-time password.'); + + const isValid = await AuthService.isOTPValid(account, req.body.otp); + if (!isValid) throw new Error('Your one-time password is incorrect.'); + + const token = await TokenService.getToken(account, AccessTokenKind.Auth); + if (token.expiry < Date.now()) throw new Error('One-time password expired'); + + await account.updateOne({ isEmailVerified: true }); + + await AuthService.getReturnUrl(account, req.interaction); + + return await oidc.interactionFinished(req, res, { login: { accountId: String(account._id) } }); + } catch (error) { + return res.render('otp', { + uid, + alert: { variant: 'danger', icon: 'exclamation-circle', message: error.message }, + params: { ...params, ...brand, claim }, + }); + } +} + +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/oidc/signin/post.ts b/apps/auth/src/app/controllers/oidc/signin/post.ts new file mode 100644 index 000000000..44a0ed45e --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/post.ts @@ -0,0 +1,15 @@ +import { Request, Response } from 'express'; +import { body, param } from 'express-validator'; +import { UnauthorizedError } from '@thxnetwork/auth/util/errors'; +import AuthService from '../../../services/AuthService'; + +const validation = [body('email').optional().isEmail(), param('uid').isMongoId()]; + +async function controller(req: Request, res: Response) { + const { email } = req.body; + if (!email) throw new UnauthorizedError('Email is required'); + + return await AuthService.redirectOTP(req, res, { email }); +} + +export default { controller, validation }; diff --git a/apps/auth/src/app/controllers/oidc/signin/retry/post.ts b/apps/auth/src/app/controllers/oidc/signin/retry/post.ts new file mode 100644 index 000000000..0cc1b8670 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/retry/post.ts @@ -0,0 +1,30 @@ +import { AccountService } from '../../../../services/AccountService'; +import { MailService } from '../../../../services/MailService'; +import { Request, Response } from 'express'; +import { AccountDocument } from '@thxnetwork/auth/models/Account'; +import { NotFoundError } from '@thxnetwork/auth/util/errors'; + +async function controller(req: Request, res: Response) { + const { params } = req.interaction; + + function renderSigninPage(variant: string, message: string) { + return res.render('signin', { + uid: req.params.uid, + params: { return_url: params.return_url }, + alert: { variant, message }, + }); + } + + try { + const account: AccountDocument = await AccountService.get(params.sub); + if (!account) throw new NotFoundError(); + + await MailService.sendOTPMail(account); + + return res.redirect(`/oidc/${req.params.uid}/signin/otp`); + } catch (error) { + return renderSigninPage('danger', error.message); + } +} + +export default { controller }; diff --git a/apps/auth/src/app/controllers/oidc/signin/signin.otp.test.ts b/apps/auth/src/app/controllers/oidc/signin/signin.otp.test.ts new file mode 100644 index 000000000..39fa75062 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/signin.otp.test.ts @@ -0,0 +1,95 @@ +import request from 'supertest'; +import app from '../../../app'; +import db from '../../../util/database'; +import bcrypt from 'bcrypt'; +import { API_URL, DASHBOARD_URL, INITIAL_ACCESS_TOKEN } from '../../../config/secrets'; +import { AccountService } from '../../../services/AccountService'; +import { mockWalletProxy } from '../../../util/jest/mock'; +import { AccessTokenKind } from '@thxnetwork/common/enums'; +import TokenService from '@thxnetwork/auth/services/TokenService'; + +const http = request.agent(app); + +describe('Sign In', () => { + const redirectUri = 'https://localhost:8082/signin-oidc'; + let uid = '', + clientId = ''; + + beforeAll(async () => { + await db.truncate(); + + const res = await http + .post('/reg') + .set({ Authorization: `Bearer ${INITIAL_ACCESS_TOKEN}` }) + .send({ + application_type: 'web', + client_name: 'THX Dashboard', + grant_types: ['authorization_code'], + redirect_uris: [redirectUri], + response_types: ['code'], + scope: 'openid pools:read pools:write withdrawals:read rewards:write deposits:read deposits:write wallets:read wallets:write', + }); + + clientId = res.body.client_id; + + mockWalletProxy(); + }); + + afterAll(async () => { + await db.disconnect(); + }); + + describe('Signup OTP', () => { + const otp = '00000', + email = 'fake.user@thx.network'; + + it('GET /authorize', async () => { + const params = new URLSearchParams({ + client_id: clientId, + redirect_uri: redirectUri, + resource: API_URL, + scope: 'openid pools:read pools:write withdrawals:read rewards:write deposits:read deposits:write wallets:read wallets:write', + response_type: 'code', + response_mode: 'query', + nonce: 'xun4kvy4mh', + return_url: DASHBOARD_URL, + }); + + const res = await http.get(`/authorize?${params.toString()}`).send(); + + expect(res.status).toEqual(303); + expect(res.header.location).toMatch(new RegExp('/oidc/.*')); + + uid = (res.header.location as string).split('/')[2]; + }); + + it('GET /oidc/:uid/signin', async () => { + const res = await http.get(`/oidc/${uid}/signin`).send(); + expect(res.status).toEqual(200); + expect(res.text).toMatch(new RegExp('.*Send one-time password*')); + }); + + it('GET /oidc/:uid/signin/otp', async () => { + const res = await http.post(`/oidc/${uid}/signin`).send(`email=${email}`); + expect(res.status).toEqual(302); + expect(res.header.location).toBe(`/oidc/${uid}/signin/otp`); + }); + + it('POST /oidc/:uid/signin/otp (incorrect OTP)', async () => { + const res = await http.post(`/oidc/${uid}/signin/otp`).send(`otp=12345`); + expect(res.status).toEqual(200); + expect(res.text).toMatch(new RegExp('.*Your one-time password is incorrect.*')); + }); + + it('POST /oidc/:uid/signin/otp (correct OTP)', async () => { + // Override the hashed OTP in db to continue with a deterministic value + const hashedOtp = await bcrypt.hash(otp, 10); + const account = await AccountService.getByEmail(email); + + await TokenService.setToken(account, { kind: AccessTokenKind.Auth, accessToken: hashedOtp }); + + const res = await http.post(`/oidc/${uid}/signin/otp`).send(`otp=${otp}`); + expect(res.status).toEqual(303); + }); + }); +}); diff --git a/apps/auth/src/app/controllers/oidc/signin/signin.router.ts b/apps/auth/src/app/controllers/oidc/signin/signin.router.ts new file mode 100644 index 000000000..f6800a367 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/signin.router.ts @@ -0,0 +1,30 @@ +import express, { urlencoded } from 'express'; +import { assertInput, assertInteraction } from '@thxnetwork/auth/middlewares'; +import ReadOTP from './otp/get'; +import CreateOTP from './otp/post'; +import Read from './get'; +import Create from './post'; +import CreateORPRetry from './retry/post'; +import rateLimit from 'express-rate-limit'; +import { API_URL, NODE_ENV } from '@thxnetwork/auth/config/secrets'; + +const router = express.Router({ mergeParams: true }); + +// Apply rate limit in production only +if (NODE_ENV === 'production' && API_URL.startsWith('https://api.')) { + router.use(rateLimit({ windowMs: 60 * 1000, max: 60 })); +} + +router.get('/', assertInteraction, Read.controller); +router.get('/otp', assertInteraction, ReadOTP.controller); +router.post('/', urlencoded({ extended: false }), assertInteraction, assertInput(Create.validation), Create.controller); +router.post( + '/otp', + urlencoded({ extended: false }), + assertInteraction, + assertInput(CreateOTP.validation), + CreateOTP.controller, +); +router.post('/resend-otp', urlencoded({ extended: false }), assertInteraction, CreateORPRetry.controller); + +export default router; diff --git a/apps/auth/src/app/controllers/oidc/signin/signin.sso.test.ts b/apps/auth/src/app/controllers/oidc/signin/signin.sso.test.ts new file mode 100644 index 000000000..23812cbc0 --- /dev/null +++ b/apps/auth/src/app/controllers/oidc/signin/signin.sso.test.ts @@ -0,0 +1,154 @@ +import { DASHBOARD_URL, GITHUB_API_ENDPOINT } from './../../../config/secrets'; +import nock from 'nock'; +import request from 'supertest'; +import { AccountVariant } from '@thxnetwork/common/enums'; +import app from '../../../app'; +import { AccountService } from '../../../services/AccountService'; +import { AccountPlanType } from '@thxnetwork/common/enums'; +import db from '../../../util/database'; +import { accountEmail } from '../../../util/jest'; +import { mockWalletProxy } from '../../../util/jest/mock'; +import { API_URL, INITIAL_ACCESS_TOKEN } from '../../../config/secrets'; + +const http = request.agent(app); + +describe('SSO Sign In', () => { + let uid = '', + CLIENT_ID = ''; + const REDIRECT_URL = DASHBOARD_URL + '/signin-oidc'; + + beforeAll(async () => { + mockWalletProxy(); + await db.truncate(); + + const res = await http + .post('/reg') + .set({ Authorization: `Bearer ${INITIAL_ACCESS_TOKEN}` }) + .send({ + application_type: 'web', + client_name: 'THX Dashboard', + grant_types: ['authorization_code'], + redirect_uris: [REDIRECT_URL], + response_types: ['code'], + scope: 'openid pools:read pools:write withdrawals:read rewards:write deposits:read deposits:write wallets:read wallets:write', + }); + + CLIENT_ID = res.body.client_id; + + const account = await AccountService.create({ + plan: AccountPlanType.Lite, + email: accountEmail, + variant: AccountVariant.EmailPassword, + }); + const params = new URLSearchParams({ + client_id: CLIENT_ID, + redirect_uri: REDIRECT_URL, + resource: API_URL, + scope: 'openid pools:read pools:write withdrawals:read rewards:write deposits:read deposits:write wallets:read wallets:write', + response_type: 'code', + response_mode: 'query', + nonce: 'xun4kvy4mh', + return_url: DASHBOARD_URL, + }); + + const authRes = await http.get(`/authorize?${params.toString()}`).send(); + + expect(authRes.status).toEqual(303); + expect(authRes.header.location).toMatch(new RegExp('/oidc/.*')); + + uid = (authRes.header.location as string).split('/')[2]; + + await account.save(); + }); + + afterAll(async () => { + await db.disconnect(); + nock.cleanAll(); + }); + + // describe('Google SSO', () => { + // beforeAll(async () => { + // nock('https://oauth2.googleapis.com/token') + // .post(/.*?/) + // .reply(200, { + // id_token: + // 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjFiZDY4NWY1ZThmYzYyZDc1ODcwNWMxZWIwZThhNzUyNGM0NzU5NzUiLCJ0eXAiOiJKV1QifQ.' + + // btoa( + // JSON.stringify({ + // iss: 'https://accounts.google.com', + // azp: '506948879165-0mkdoln16052qb4gb9318h5hv8rntnv3.apps.googleusercontent.com', + // aud: '506948879165-0mkdoln16052qb4gb9318h5hv8rntnv3.apps.googleusercontent.com', + // sub: '116780302581790032921', + // email: accountEmail, + // email_verified: true, + // at_hash: 'PGVG213L9h_mvf8AFAsVtQ', + // iat: 1657545884, + // exp: 1657549484, + // }), + // ), + // }); // mock response for account create method + // }); + // it('GET /oidc/callback/google', async () => { + // const params = new URLSearchParams({ + // code: 'thisnotgonnawork', + // state: Buffer.from(JSON.stringify({ uid })).toString('base64'), + // }); + // const res = await http.get('/oidc/callback/google?' + params.toString()); + + // expect(res.status).toBe(302); + // expect(res.headers['location']).toContain('/authorize/'); + // }); + // }); + + // describe('Twitter SSO', () => { + // beforeAll(async () => { + // nock(TWITTER_API_ENDPOINT + '/oauth2/token') + // .post(/.*?/) + // .reply(200, { + // accessToken: 'thisnotgonnawork', + // expires_in: 60000, + // }); + // nock(TWITTER_API_ENDPOINT + '/users/me') + // .get(/.*?/) + // .reply(200, { + // data: { + // id: 'thisnotgonnawork', + // }, + // }); + // }); + + // it('GET /oidc/callback/twitter', async () => { + // const params = new URLSearchParams({ + // code: 'thisnotgonnawork', + // state: Buffer.from(JSON.stringify({ uid })).toString('base64'), + // }); + // const res = await http.get('/oidc/callback/twitter?' + params.toString()); + + // expect(res.status).toBe(302); + // expect(res.headers['location']).toContain('/authorize/'); + // }); + // }); + + describe('Github SSO', () => { + beforeAll(async () => { + nock('https://github.com/login/oauth/access_token').post(/.*?/).reply(200, 'access_token=thisnotgonnawork'); + + nock(GITHUB_API_ENDPOINT + '/user') + .get(/.*?/) + .reply(200, { + login: 'GarfDev', + }); + }); + + it('GET /oidc/callback/github', async () => { + const params = new URLSearchParams({ + code: 'thisnotgonnawork', + state: Buffer.from(JSON.stringify({ uid })).toString('base64'), + }); + const res = await http.get('/oidc/callback/github?' + params.toString()); + + expect(res.status).toBe(302); + expect(res.headers['location']).toContain('/authorize/'); + }); + }); +}); diff --git a/apps/auth/src/app/middlewares/assertAuthorization.ts b/apps/auth/src/app/middlewares/assertAuthorization.ts new file mode 100644 index 000000000..367a44eeb --- /dev/null +++ b/apps/auth/src/app/middlewares/assertAuthorization.ts @@ -0,0 +1,6 @@ +import { Request, Response, NextFunction } from 'express'; + +export async function assertAuthorization(req: Request, res: Response, next: NextFunction) { + if (req.interaction && !req.interaction.session) throw new Error('Not authorized'); + next(); +} diff --git a/apps/auth/src/app/middlewares/assertInput.ts b/apps/auth/src/app/middlewares/assertInput.ts new file mode 100644 index 000000000..d0deb765f --- /dev/null +++ b/apps/auth/src/app/middlewares/assertInput.ts @@ -0,0 +1,18 @@ +import { validationResult } from 'express-validator'; +import { Request, Response, NextFunction } from 'express'; + +export function assertInput(validations: any) { + return async function (req: Request, res: Response, next: NextFunction) { + await Promise.all(validations.map((validation: any) => validation.run(req))); + + const errors = validationResult(req); + + if (errors.isEmpty()) return next(); + if (!req.interaction) throw new Error('no interaction'); + + req.interaction.alert = { variant: 'danger', message: errors }; + await req.interaction.save(Date.now() + 1000); + + next(); + }; +} diff --git a/apps/auth/src/app/middlewares/assertInteraction.ts b/apps/auth/src/app/middlewares/assertInteraction.ts new file mode 100644 index 000000000..508eeb6d7 --- /dev/null +++ b/apps/auth/src/app/middlewares/assertInteraction.ts @@ -0,0 +1,9 @@ +import { Request, Response, NextFunction } from 'express'; +import { oidc } from '../util/oidc'; + +export async function assertInteraction(req: Request, res: Response, next: NextFunction) { + const interaction = await oidc.interactionDetails(req, res); + if (!interaction) throw new Error('Could not find the interaction.'); + req.interaction = interaction; + next(); +} diff --git a/apps/auth/src/app/middlewares/cors.ts b/apps/auth/src/app/middlewares/cors.ts new file mode 100644 index 000000000..57e8eb9e1 --- /dev/null +++ b/apps/auth/src/app/middlewares/cors.ts @@ -0,0 +1,24 @@ +import cors from 'cors'; +import { AUTH_URL, WALLET_URL, DASHBOARD_URL, WIDGET_URL, PUBLIC_URL } from '../config/secrets'; + +export const corsHandler = cors(async (req: any, callback: any) => { + const origin = req.header('Origin'); + const allowedOrigins = [ + AUTH_URL, + WALLET_URL, + DASHBOARD_URL, + WIDGET_URL, + PUBLIC_URL, + 'https://app.thx.network', + 'https://dev-app.thx.network', + ]; + + if (!origin || allowedOrigins.indexOf(origin) > -1) { + callback(null, { + credentials: true, + origin: '*', + }); + } else { + callback(null); + } +}); diff --git a/apps/auth/src/app/middlewares/errorLogger.ts b/apps/auth/src/app/middlewares/errorLogger.ts new file mode 100644 index 000000000..0c190cc85 --- /dev/null +++ b/apps/auth/src/app/middlewares/errorLogger.ts @@ -0,0 +1,11 @@ +import { THXHttpError } from '../util/errors'; +import { logger } from '../util/logger'; +import { NextFunction, Request, Response } from 'express'; + +export const errorLogger = (error: Error, req: Request, res: Response, next: NextFunction) => { + if (!(error instanceof THXHttpError)) { + logger.error('Error caught:', error); + } + + next(error); +}; diff --git a/apps/auth/src/app/middlewares/errorNormalizer.ts b/apps/auth/src/app/middlewares/errorNormalizer.ts new file mode 100644 index 000000000..aebbb5a3f --- /dev/null +++ b/apps/auth/src/app/middlewares/errorNormalizer.ts @@ -0,0 +1,11 @@ +import { NextFunction, Request, Response } from 'express'; +import { UnauthorizedError } from '../util/errors'; +import { UnauthorizedError as JWTUnauthorizedError } from 'express-jwt'; + +export const errorNormalizer = (error: Error, _req: Request, _res: Response, next: NextFunction) => { + if (error instanceof JWTUnauthorizedError) { + return next(new UnauthorizedError(error.message)); + } + + next(error); +}; diff --git a/apps/auth/src/app/middlewares/errorOutput.ts b/apps/auth/src/app/middlewares/errorOutput.ts new file mode 100644 index 000000000..4b6c1309a --- /dev/null +++ b/apps/auth/src/app/middlewares/errorOutput.ts @@ -0,0 +1,41 @@ +import { NextFunction, Request, Response } from 'express'; +import { THXHttpError } from '../util/errors'; +import { NODE_ENV } from '../config/secrets'; + +interface ErrorResponse { + error: { + message: string; + error?: Error; + rootMessage?: string; + stack?: string; + }; +} + +const isJsonPath = (path: string): boolean => { + // This determines for which prefixes a json error is presented. + for (const prefix of ['/account', '/health']) { + if (path.startsWith(prefix)) return true; + } + return false; +}; + +// Error handler needs to have 4 arguments. +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const errorOutput = (error: any, req: Request, res: Response, next: NextFunction) => { + let status = 500; + const response: ErrorResponse = { error: { message: error.message || 'Unable to fulfill request' } }; + if (error instanceof THXHttpError || error.status) { + status = error.status; + response.error.message = error.message; + } else if (NODE_ENV !== 'production') { + response.error.error = error; + response.error.stack = error.stack; + } + res.status(status); + if (isJsonPath(req.path)) { + res.json(response); + } else { + const returnUrl = req.interaction ? req.interaction.params.return_url : ''; + res.render('error', { returnUrl, alert: { variant: 'danger', message: response.error.message } }); + } +}; diff --git a/apps/auth/src/app/middlewares/index.ts b/apps/auth/src/app/middlewares/index.ts new file mode 100644 index 000000000..5243b4241 --- /dev/null +++ b/apps/auth/src/app/middlewares/index.ts @@ -0,0 +1,10 @@ +export * from './errorOutput'; +export * from './errorLogger'; +export * from './errorNormalizer'; +export * from './notFoundHandler'; +export * from './cors'; +export * from './validateJwt'; +export * from './assertInteraction'; +export * from './assertAuthorization'; +export * from './assertInput'; +export * from './morgan'; diff --git a/apps/auth/src/app/middlewares/morgan.ts b/apps/auth/src/app/middlewares/morgan.ts new file mode 100644 index 000000000..994de436b --- /dev/null +++ b/apps/auth/src/app/middlewares/morgan.ts @@ -0,0 +1,16 @@ +import morgan from 'morgan'; +import { logger } from '../util/logger'; +import { NODE_ENV } from '../config/secrets'; + +const stream = { + write: (message) => logger.http(message), +}; + +const skip = () => { + return ['development', 'test'].includes(NODE_ENV || 'development'); +}; + +export default morgan( + ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" - :response-time ms', + { stream, skip }, +); diff --git a/apps/auth/src/app/middlewares/notFoundHandler.ts b/apps/auth/src/app/middlewares/notFoundHandler.ts new file mode 100644 index 000000000..7ea7e3684 --- /dev/null +++ b/apps/auth/src/app/middlewares/notFoundHandler.ts @@ -0,0 +1,6 @@ +import { NextFunction, Request, Response } from 'express'; +import { NotFoundError } from '../util/errors'; + +export const notFoundHandler = (req: Request, res: Response, next: NextFunction) => { + next(new NotFoundError()); +}; diff --git a/apps/auth/src/app/middlewares/validateJwt.ts b/apps/auth/src/app/middlewares/validateJwt.ts new file mode 100644 index 000000000..69ddc1a6a --- /dev/null +++ b/apps/auth/src/app/middlewares/validateJwt.ts @@ -0,0 +1,17 @@ +import jwksRsa from 'jwks-rsa'; +import expressJwtPermissions from 'express-jwt-permissions'; +import { expressjwt } from 'express-jwt'; +import { AUTH_URL } from '../config/secrets'; + +export const validateJwt = expressjwt({ + secret: jwksRsa.expressJwtSecret({ + cache: true, + rateLimit: false, + jwksRequestsPerMinute: 10, + jwksUri: `${AUTH_URL}/jwks`, // Unnecessary, keys are provided through getKeysInterceptor. + }), + issuer: AUTH_URL, + algorithms: ['RS256'], +}); + +export const guard = expressJwtPermissions({ requestProperty: 'auth', permissionsProperty: 'scope' }); diff --git a/apps/auth/src/app/migrations/20240325102059-account-plans.js b/apps/auth/src/app/migrations/20240325102059-account-plans.js new file mode 100644 index 000000000..6266315f3 --- /dev/null +++ b/apps/auth/src/app/migrations/20240325102059-account-plans.js @@ -0,0 +1,18 @@ +module.exports = { + async up(db) { + const accountColl = db.collection('accounts'); + + // Change all existing Free (0) and Basic (1) plans to Lite (0) and also make all undefined plans Lite (0) + await accountColl.updateOne( + { $or: [{ plan: 0 }, { plan: 1 }, { plan: null }, { plan: { $exists: false } }] }, + { $set: { plan: 0 } }, + ); + + // Change all existing Premium (2) plans to Premium (1) + await accountColl.updateOne({ $or: [{ plan: 2 }] }, { $set: { plan: 1 } }); + }, + + async down() { + // + }, +}; diff --git a/apps/auth/src/app/models/Account.ts b/apps/auth/src/app/models/Account.ts new file mode 100644 index 000000000..fb5ab41d5 --- /dev/null +++ b/apps/auth/src/app/models/Account.ts @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; + +export type AccountDocument = mongoose.Document & TAccount; + +const accountSchema = new mongoose.Schema( + { + username: { type: String, maxLength: 255 }, + active: Boolean, + isEmailVerified: Boolean, + firstName: { type: String, maxLength: 255 }, + lastName: { type: String, maxLength: 255 }, + profileImg: String, + website: { type: String, maxLength: 255 }, + organisation: { type: String, maxLength: 255 }, + plan: Number, + // email.sparse allows the value to be null and unique if defined + email: { type: String, unique: true, sparse: true, maxLength: 255 }, + // address.sparse allows the value to be null and unique if defined + address: { type: String, unique: true, sparse: true }, + variant: Number, + otpSecret: String, + acceptTermsPrivacy: Boolean, + acceptUpdates: Boolean, + role: String, + identity: String, + goal: [String], + }, + { timestamps: true }, +); + +export const Account = mongoose.model<AccountDocument>('Account', accountSchema); diff --git a/apps/auth/src/app/models/Client.ts b/apps/auth/src/app/models/Client.ts new file mode 100644 index 000000000..2d70f9ea5 --- /dev/null +++ b/apps/auth/src/app/models/Client.ts @@ -0,0 +1,20 @@ +import mongoose from 'mongoose'; + +export type ClientDocument = mongoose.Document & { + _id: string; + payload: { + request_uris: string[]; + }; +}; + +const clientSchema = new mongoose.Schema( + { + _id: String, + payload: { + request_uris: [String], + }, + }, + { timestamps: false }, +); + +export const Client = mongoose.model<ClientDocument>('Client', clientSchema, 'client'); diff --git a/apps/auth/src/app/models/Token.ts b/apps/auth/src/app/models/Token.ts new file mode 100644 index 000000000..edf0b9cb9 --- /dev/null +++ b/apps/auth/src/app/models/Token.ts @@ -0,0 +1,44 @@ +import mongoose from 'mongoose'; +import { encryptString } from '../util/encrypt'; +import { SECURE_KEY } from '../config/secrets'; +import { decryptString } from '../util/decrypt'; + +export type TokenDocument = mongoose.Document & TToken; + +const tokenSchema = new mongoose.Schema( + { + sub: String, + kind: String, + accessTokenEncrypted: String, + refreshTokenEncrypted: String, + expiry: Number, + userId: String, + scopes: [String], + metadata: Object, + }, + { timestamps: true }, +); + +tokenSchema.pre(['save', 'findOneAndUpdate'], function (next) { + const accessToken = this.get('accessToken'); + const refreshToken = this.get('refreshToken'); + if (accessToken) { + const accessTokenEncrypted = encryptString(accessToken, SECURE_KEY); + this.set('accessTokenEncrypted', accessTokenEncrypted); + } + if (refreshToken) { + const refreshTokenEncrypted = encryptString(refreshToken, SECURE_KEY); + this.set('refreshTokenEncrypted', refreshTokenEncrypted); + } + next(); +}); + +tokenSchema.virtual('accessToken').get(function () { + return this.accessTokenEncrypted && decryptString(this.accessTokenEncrypted, SECURE_KEY); +}); + +tokenSchema.virtual('refreshToken').get(function () { + return this.refreshTokenEncrypted && decryptString(this.refreshTokenEncrypted, SECURE_KEY); +}); + +export const Token = mongoose.model<TokenDocument>('Token', tokenSchema, 'tokens'); diff --git a/apps/auth/src/app/proxies/BrandProxy.ts b/apps/auth/src/app/proxies/BrandProxy.ts new file mode 100644 index 000000000..7444527db --- /dev/null +++ b/apps/auth/src/app/proxies/BrandProxy.ts @@ -0,0 +1,15 @@ +import { apiClient } from '../util/api'; + +export default { + get: async (poolId: string) => { + const r = await apiClient({ + method: 'GET', + url: '/v1/brands', + headers: { + 'X-PoolId': poolId, + }, + }); + + return r.data; + }, +}; diff --git a/apps/auth/src/app/proxies/ClaimProxy.ts b/apps/auth/src/app/proxies/ClaimProxy.ts new file mode 100644 index 000000000..c0c590780 --- /dev/null +++ b/apps/auth/src/app/proxies/ClaimProxy.ts @@ -0,0 +1,14 @@ +import { apiClient, getAuthAccessToken } from '../util/api'; + +export default { + get: async (uuid: string) => { + const { data } = await apiClient({ + method: 'GET', + url: `/v1/qr-codes/${uuid}`, + headers: { + Authorization: await getAuthAccessToken(), + }, + }); + return data; + }, +}; diff --git a/apps/auth/src/app/proxies/PoolProxy.ts b/apps/auth/src/app/proxies/PoolProxy.ts new file mode 100644 index 000000000..f08db30dc --- /dev/null +++ b/apps/auth/src/app/proxies/PoolProxy.ts @@ -0,0 +1,21 @@ +import { AccountDocument } from '../models/Account'; +import { apiClient } from '../util/api'; + +export default { + transferOwnership: async (account: AccountDocument, poolId: string, token: string) => { + const { data } = await apiClient({ + method: 'POST', + url: `/v1/pools/${poolId}/transfers`, + data: { token, sub: String(account._id) }, + }); + return data; + }, + + getPool: async (poolId: string) => { + const { data } = await apiClient({ + method: 'GET', + url: `/v1/pools/${poolId}`, + }); + return data; + }, +}; diff --git a/apps/auth/src/app/proxies/UploadProxy.ts b/apps/auth/src/app/proxies/UploadProxy.ts new file mode 100644 index 000000000..e2406af1f --- /dev/null +++ b/apps/auth/src/app/proxies/UploadProxy.ts @@ -0,0 +1,25 @@ +import FormData from 'form-data'; + +import { apiClient } from '../util/api'; + +export default { + post: async (file: Express.Multer.File): Promise<string> => { + const form = new FormData({ readable: true, dataSize: file.size }); + + form.append('file', file.buffer, { + contentType: file.mimetype, + filename: file.originalname, + }); + + const r = await apiClient({ + url: '/v1/upload', + method: 'PUT', + headers: { + 'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`, + }, + data: form, + }); + + return r.data.publicUrl as string; + }, +}; diff --git a/apps/auth/src/app/services/AccountService.ts b/apps/auth/src/app/services/AccountService.ts new file mode 100644 index 000000000..de569de4c --- /dev/null +++ b/apps/auth/src/app/services/AccountService.ts @@ -0,0 +1,115 @@ +import { Account, AccountDocument } from '../models/Account'; +import { AccountVariant, AccountPlanType } from '@thxnetwork/common/enums'; +import { generateUsername } from 'unique-username-generator'; +import { toChecksumAddress } from 'web3-utils'; +import { BadRequestError } from '../util/errors'; +import { MailService } from './MailService'; +import { WIDGET_URL } from '../config/secrets'; +import { accountVariantProviderMap } from '@thxnetwork/common/maps'; +import TokenService from './TokenService'; + +export class AccountService { + static create(data: Partial<AccountDocument>) { + return Account.create({ + plan: AccountPlanType.Lite, + username: generateUsername(), + ...data, + email: data.email && data.email.toLowerCase(), + isEmailVerified: false, + active: true, + }); + } + + static async update(account: AccountDocument, data: Partial<AccountDocument & { returnUrl?: string }>) { + // Checksum address + if (data.address) { + data.address = toChecksumAddress(data.address); + } + + // Test username when changing username + if (data.username && account.username !== data.username) { + const isUsed = await Account.exists({ + username: data.username, + _id: { $ne: data._id, $exists: true }, + }); + if (isUsed) throw new BadRequestError('Username already in use.'); + } + + // Send verification email when changing email + if (data.email) { + // Only check if email is different than the current one + if (data.email !== account.email) { + const isUsed = await Account.exists({ + email: data.email, + _id: { $ne: String(account._id), $exists: true }, + }); + if (isUsed) throw new BadRequestError('Email already in use.'); + data.isEmailVerified = false; + } + + // Always send mail in case this is a retry + await MailService.sendVerificationEmail(account, data.email, WIDGET_URL); + } + + return await Account.findByIdAndUpdate(account._id, data, { new: true }); + } + + static get(sub: string) { + return Account.findById(sub); + } + + static find(query: { _id: string[] }) { + return Account.find({ _id: { $in: query._id } }); + } + + static findByQuery({ query }: { query: string }) { + return Account.find({ $or: [{ username: new RegExp(query, 'i') }, { email: new RegExp(query, 'i') }] }); + } + + static getByEmail(email: string) { + return Account.findOne({ email }); + } + + static getByIdentity(identity: string) { + return Account.findOne({ identity }); + } + + static getByAddress(address: string) { + return Account.findOne({ address }); + } + + static async remove(id: string) { + await Account.deleteOne({ _id: id }); + } + + static findAccountForSession(session: { accountId: string }) { + return Account.findById(session.accountId); + } + + static findAccountForEmail(email: string) { + return Account.findOne({ email: email.toLowerCase() }); + } + + static async findAccountForToken(variant: AccountVariant, tokenInfo: Partial<{ userId: string }>) { + const kind = accountVariantProviderMap[variant]; + const token = await TokenService.findTokenForUserId(tokenInfo.userId, kind); + if (!token) return; + + return await Account.findById(token.sub); + } + + static async findAccountForAddress(address: string) { + const checksummedAddress = toChecksumAddress(address); + // Checking for non checksummed as well in order to avoid issues with existing data in db + const account = await Account.findOne({ + $or: [{ address: checksummedAddress }, { address }], + variant: AccountVariant.Metamask, + }); + if (account) return account; + return await Account.create({ + variant: AccountVariant.Metamask, + plan: AccountPlanType.Lite, + address, + }); + } +} diff --git a/apps/auth/src/app/services/AuthService.ts b/apps/auth/src/app/services/AuthService.ts new file mode 100644 index 000000000..f6abba5cf --- /dev/null +++ b/apps/auth/src/app/services/AuthService.ts @@ -0,0 +1,216 @@ +import { Request, Response } from 'express'; +import { Account, AccountDocument } from '../models/Account'; +import { SUCCESS_SIGNUP_COMPLETED } from '../util/messages'; +import { AccessTokenKind, AccountVariant, AccountPlanType, OAuthRequiredScopes } from '@thxnetwork/common/enums'; +import bcrypt from 'bcrypt'; +import { AccountService } from './AccountService'; +import { Token } from '../models/Token'; +import { UnauthorizedError } from '@thxnetwork/auth/util/errors'; +import { oidc } from '@thxnetwork/auth/util/oidc'; +import { MailService } from './MailService'; +import TokenService from './TokenService'; +import EthereumService from './EthereumService'; + +export default class AuthService { + static async connect(interaction: TInteraction, tokenInfo: Partial<TToken>, variant: AccountVariant) { + let account: AccountDocument; + const { session, params } = interaction; + + // Find account for active session + if (session && session.accountId) { + account = await AccountService.findAccountForSession(session); + } + + // Find account for userId + else if (tokenInfo) { + account = await AccountService.findAccountForToken(variant, tokenInfo); + } + + // If no match, create the account + if (!account) { + account = await AccountService.create({ variant, plan: params.signup_plan }); + } + + // Connect token to account + await TokenService.connect(account, tokenInfo); + + return account; + } + + static async signup(data: { email?: string; plan: AccountPlanType; variant: AccountVariant; active: boolean }) { + let account: AccountDocument; + + if (data.email) { + account = await Account.findOne({ email: data.email, active: false }); + } + + if (!account) { + account = await AccountService.create({ + email: data.email, + plan: data.plan, + }); + } + + account.active = data.active; + account.email = data.email; + account.variant = data.variant; + account.plan = data.plan; + + return await account.save(); + } + + static async redirectSSO(_req: Request, res: Response, { uid, variant }: { uid: string; variant: AccountVariant }) { + const map = { + [AccountVariant.SSOGoogle]: { + kind: AccessTokenKind.Google, + scopes: OAuthRequiredScopes.GoogleAuth, + }, + [AccountVariant.SSODiscord]: { + kind: AccessTokenKind.Discord, + scopes: OAuthRequiredScopes.DiscordAuth, + }, + [AccountVariant.SSOTwitter]: { + kind: AccessTokenKind.Twitter, + scopes: OAuthRequiredScopes.TwitterAuth, + }, + [AccountVariant.SSOTwitch]: { + kind: AccessTokenKind.Twitch, + scopes: OAuthRequiredScopes.TwitchAuth, + }, + [AccountVariant.SSOGithub]: { + kind: AccessTokenKind.Github, + scopes: OAuthRequiredScopes.GithubAuth, + }, + }; + const { kind, scopes } = map[variant]; + + const url = TokenService.getLoginURL({ uid, kind, scopes }); + + return res.redirect(url); + } + static async redirectWalletConnect( + req: Request, + res: Response, + { message, signature }: { message: string; signature: string }, + ) { + // If signed auth request is available recover the address from the signature and lookup user + if (!message || !signature) { + throw new UnauthorizedError('Signed message and signature are required for Metamask login.'); + } + + const address = EthereumService.recoverSigner(decodeURIComponent(message), signature); + if (!address) throw new UnauthorizedError('Could not recover address from signed message.'); + + const account = await AccountService.findAccountForAddress(address); + if (!account) throw new UnauthorizedError('Account not found or created.'); + + return await oidc.interactionFinished(req, res, { login: { accountId: String(account._id) } }); + } + + static async getOTPAttempt(req: Request) { + const { params } = req.interaction; + + // Store OTP attempt in interaction + req.interaction.params.otpAttempts = (params.otpAttempts || 0) + 1; + + // Interaction TTL is set to 10min and will expire after + await req.interaction.save(Date.now() + 10 * 60 * 1000); + + return req.interaction.params.otpAttempts; + } + + static async redirectOTP(req: Request, res: Response, { email }: { email: string }) { + const { params } = req.interaction; + let account = await AccountService.findAccountForEmail(email); + + // Create and return account if none found the given email + if (!account) { + const variant = AccountVariant.EmailPassword; + const plan = params.signup_plan ? Number(params.signup_plan) : AccountPlanType.Lite; + + account = await AccountService.create({ email, plan, variant }); + } + + // Send email using SES + await MailService.sendOTPMail(account); + + // Store the sub in the interaction so we can lookup the hashed OTP later + req.interaction.params.sub = String(account._id); + req.interaction.params.email = email; + + // Interaction TTL is set to 10min and will expire after + await req.interaction.save(Date.now() + 10 * 60 * 1000); + + const redirectURL = `/oidc/${req.params.uid}/signin/otp`; + + return res.redirect(redirectURL); + } + + static async isOTPValid(account: AccountDocument, otp: string): Promise<boolean> { + const token = await TokenService.getToken(account, AccessTokenKind.Auth); + if (!token) return; + return await bcrypt.compare(otp, token.accessToken); + } + + static async verifyEmailToken(verifyEmailToken: string) { + const token = await Token.findOne({ + kind: AccessTokenKind.VerifyEmail, + accessTokenEncrypted: verifyEmailToken, + }); + if (!token) return { error: 'Verification request not found.' }; + if (token && token.expiry < Date.now()) return { error: 'Verification request is expired.' }; + + const account = await Account.findById(token.sub); + if (!account) return { error: 'Account not found' }; + + await TokenService.unsetToken(account, AccessTokenKind.VerifyEmail); + + await Account.findByIdAndUpdate(account._id, { isEmailVerified: true }); + + return { result: SUCCESS_SIGNUP_COMPLETED, account }; + } + + static async redirectCallback(req: Request) { + // Get code from url + const code = req.query.code as string; + // Throw error if not exists + if (!code) throw new UnauthorizedError('Could not find code in query'); + + const stateBase64String = req.query.state as string; + const stateSerialized = Buffer.from(stateBase64String, 'base64').toString(); + const { uid } = JSON.parse(stateSerialized); + + // Throw if no uid is present in state object + if (!uid) throw new UnauthorizedError('Could not find uid in state object'); + + // See if interaction still exists and throw if not + const interaction = await oidc.Interaction.find(uid); + if (!interaction) throw new UnauthorizedError('Your session has expired.'); + + return { interaction, code }; + } + + static async getReturn(interaction, account: AccountDocument) { + if (!account) throw new UnauthorizedError('Could not find or create an account'); + + // Update interaction with login state + interaction.result = { login: { accountId: String(account._id) } }; + await interaction.save(Date.now() + 10000); + + return await this.getReturnUrl(account, interaction); + } + + static async getReturnUrl( + account: AccountDocument, + { params, returnTo, prompt }: { params: any; returnTo: string; prompt: any }, + ) { + let returnUrl = returnTo; + // Connect prompts already have a session and will therefor not continue the + // regular auth signin flow used during SSO + if (prompt && prompt.name === 'connect') { + returnUrl = params.display === 'popup' ? params.redirect_uri : params.return_url; + } + + return returnUrl; + } +} diff --git a/apps/auth/src/app/services/EthereumService.ts b/apps/auth/src/app/services/EthereumService.ts new file mode 100644 index 000000000..c6746a428 --- /dev/null +++ b/apps/auth/src/app/services/EthereumService.ts @@ -0,0 +1,45 @@ +import { arrayify, computeAddress, hashMessage, recoverPublicKey } from 'ethers/lib/utils'; +import { SignTypedDataVersion, recoverTypedSignature } from '@metamask/eth-sig-util'; + +export const AUTH_REQUEST_TYPED_MESSAGE = + "Welcome! Please make sure you have selected your preferred account and sign this message to verify it's ownership."; + +export default class EthereumService { + static createTypedMessage(message: string, app: string, nonce: string) { + return JSON.stringify({ + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + ], + TypedMessage: [ + { name: 'message', type: 'string' }, + { name: 'app', type: 'string' }, + { name: 'nonce', type: 'string' }, + ], + }, + domain: { + name: 'THX Network', + version: '1', + }, + primaryType: 'TypedMessage', + message: { + message, + app, + nonce, + }, + }); + } + + static recoverAddress(message: string, signature: string) { + return recoverTypedSignature({ + data: JSON.parse(message), + signature, + version: 'V3' as SignTypedDataVersion, + }); + } + + static recoverSigner(message: string, signature: string) { + return computeAddress(recoverPublicKey(arrayify(hashMessage(message)), signature)); + } +} diff --git a/apps/auth/src/app/services/MailService.ts b/apps/auth/src/app/services/MailService.ts new file mode 100644 index 000000000..95f1124bd --- /dev/null +++ b/apps/auth/src/app/services/MailService.ts @@ -0,0 +1,85 @@ +import ejs from 'ejs'; +import path from 'path'; +import bcrypt from 'bcrypt'; +import crypto from 'crypto'; +import { AccountDocument } from '../models/Account'; +import { createRandomToken } from '../util/tokens'; +import { assetsPath } from '../util/path'; +import { AccessTokenKind } from '@thxnetwork/common/enums/AccessTokenKind'; +import { get24HoursExpiryTimestamp } from '../util/time'; +import { + AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY, + AUTH_URL, + WALLET_URL, + NODE_ENV, + CYPRESS_EMAIL, +} from '../config/secrets'; +import { sendMail } from '@thxnetwork/common/mail'; +import { logger } from '../util/logger'; +import TokenService from './TokenService'; + +const mailTemplatePath = path.join(assetsPath, 'views', 'mail'); + +function createOTP(account: AccountDocument) { + return account.email === CYPRESS_EMAIL + ? '00000' + : Array.from({ length: 5 }) + .map(() => crypto.randomInt(0, 10)) + .join(''); +} + +export class MailService { + static sendMail(to: string, subject: string, html: string, link = '') { + if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY || NODE_ENV === 'test' || CYPRESS_EMAIL === to) { + logger.debug({ message: 'Not sending e-mail', link }); + return; + } + sendMail(to, subject, html); + } + + static async sendVerificationEmail(account: AccountDocument, email: string, returnUrl: string) { + const accessToken = createRandomToken(); + const expiry = get24HoursExpiryTimestamp(); + const token = await TokenService.setToken(account, { + kind: AccessTokenKind.VerifyEmail, + accessToken, + expiry, + }); + const verifyURL = new URL(returnUrl); + verifyURL.pathname = '/verify_email'; + verifyURL.searchParams.append('verifyEmailToken', token.accessTokenEncrypted); + verifyURL.searchParams.append('return_url', returnUrl); + + const html = await ejs.renderFile( + path.join(mailTemplatePath, '/email-verify.ejs'), + { verifyURL: verifyURL.toString(), returnUrl, baseUrl: AUTH_URL }, + { async: true }, + ); + + this.sendMail( + email, + 'Please complete the e-mail verification for your THX Account', + html, + verifyURL.toString(), + ); + } + + static async sendOTPMail(account: AccountDocument) { + const otp = createOTP(account); + const hashedOtp = await bcrypt.hash(otp, 10); + const html = await ejs.renderFile( + path.join(mailTemplatePath, 'email-otp.ejs'), + { otp, returnUrl: WALLET_URL, baseUrl: AUTH_URL }, + { async: true }, + ); + + this.sendMail(account.email, 'Request: Sign in', html); + + await TokenService.setToken(account, { + kind: AccessTokenKind.Auth, + accessToken: hashedOtp, + expiry: Date.now() + 60 * 60 * 1000, // 60 minutes + }); + } +} diff --git a/apps/auth/src/app/services/OAuthDiscordService.ts b/apps/auth/src/app/services/OAuthDiscordService.ts new file mode 100644 index 000000000..c254d6f28 --- /dev/null +++ b/apps/auth/src/app/services/OAuthDiscordService.ts @@ -0,0 +1,103 @@ +import { AUTH_URL, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET } from '../config/secrets'; +import { AccessTokenKind, OAuthDiscordScope } from '@thxnetwork/common/enums/AccessTokenKind'; +import { discordClient } from '../util/axios'; +import { Token, TokenDocument } from '../models/Token'; +import { IOAuthService } from './interfaces/IOAuthService'; + +export default class DiscordService implements IOAuthService { + getLoginURL({ uid, scopes }: { uid: string; scopes: OAuthDiscordScope[] }): string { + const state = Buffer.from(JSON.stringify({ uid })).toString('base64'); + const url = new URL('https://discord.com/oauth2/authorize'); + url.searchParams.append('state', state); + url.searchParams.append('response_type', 'code'); + url.searchParams.append('client_id', DISCORD_CLIENT_ID); + url.searchParams.append('redirect_uri', AUTH_URL + '/oidc/callback/discord'); + url.searchParams.append('scope', scopes.join(' ')); + + return url.toString(); + } + + async requestToken(code: string) { + const body = new URLSearchParams(); + body.append('code', code); + body.append('grant_type', 'authorization_code'); + body.append('redirect_uri', AUTH_URL + '/oidc/callback/discord'); + body.append('client_secret', DISCORD_CLIENT_SECRET); + body.append('client_id', DISCORD_CLIENT_ID); + + const { data } = await discordClient({ + url: 'https://discord.com/api/oauth2/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: body, + }); + const user = await this.getUser(data.access_token); + + return { + kind: AccessTokenKind.Discord, + accessToken: data.access_token, + refreshToken: data.refresh_token, + expiry: Date.now() + Number(data.expires_in) * 1000, + scopes: data.scope.split(' '), + userId: user.id, + }; + } + + async refreshToken(token: TokenDocument) { + const body = new URLSearchParams(); + body.append('grant_type', 'refresh_token'); + body.append('refresh_token', token.refreshToken); + body.append('client_secret', DISCORD_CLIENT_SECRET); + body.append('client_id', DISCORD_CLIENT_ID); + + const { data } = await discordClient({ + url: 'https://discord.com/api/oauth2/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: body, + }); + + return await Token.findByIdAndUpdate( + token._id, + { + accessToken: data.access_token, + refreshToken: data.refresh_token, + expiry: data.expires_in && Date.now() + Number(data.expires_in) * 1000, + }, + { new: true }, + ); + } + + async revokeToken(token: TokenDocument): Promise<void> { + const body = new URLSearchParams(); + body.append('client_secret', DISCORD_CLIENT_SECRET); + body.append('client_id', DISCORD_CLIENT_ID); + body.append('token', token.accessToken); + + await discordClient({ + url: 'https://discord.com/api/oauth2/token/revoke', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: body, + }); + } + + private async getUser(accessToken: string) { + const { data } = await discordClient({ + url: '/oauth2/@me', + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${accessToken}`, + }, + }); + return data.user; + } +} diff --git a/apps/auth/src/app/services/OAuthGithubService.ts b/apps/auth/src/app/services/OAuthGithubService.ts new file mode 100644 index 000000000..b981e4b5a --- /dev/null +++ b/apps/auth/src/app/services/OAuthGithubService.ts @@ -0,0 +1,77 @@ +import { URLSearchParams } from 'url'; +import { AUTH_URL, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } from '@thxnetwork/auth/config/secrets'; +import { githubClient } from '../util/axios'; +import { AccessTokenKind, OAuthGithubScope } from '@thxnetwork/common/enums/AccessTokenKind'; +import { IOAuthService } from './interfaces/IOAuthService'; +import { TokenDocument } from '../models/Token'; + +export default class GithubService implements IOAuthService { + getLoginURL({ uid, scopes }: { uid: string; scopes: OAuthGithubScope[] }) { + const state = Buffer.from(JSON.stringify({ uid })).toString('base64'); + const url = new URL('https://github.com/login/oauth/authorize'); + url.searchParams.append('state', state); + url.searchParams.append('allow_signup', 'true'); + url.searchParams.append('client_id', GITHUB_CLIENT_ID); + url.searchParams.append('redirect_uri', AUTH_URL + '/oidc/callback/github'); + url.searchParams.append('scope', scopes.join(' ')); + + return url.toString(); + } + + async requestToken(code: string) { + const { data } = await githubClient({ + url: 'https://github.com/login/oauth/access_token', + method: 'POST', + data: { + code, + redirect_uri: AUTH_URL + '/oidc/callback/github', + client_secret: GITHUB_CLIENT_SECRET, + client_id: GITHUB_CLIENT_ID, + }, + }); + + const search = new URLSearchParams(data); + const accessToken = search.get('access_token'); + const refreshToken = search.get('refresh_token'); + const expiresIn = search.get('expires_in'); + const expiry = expiresIn && Date.now() + Number(expiresIn) * 1000; + const user = await this.getUser(accessToken); + + return { + kind: AccessTokenKind.Github, + accessToken, + refreshToken, + expiry, + userId: user.id, + }; + } + + async refreshToken(token: TokenDocument) { + const { data } = await githubClient({ + url: 'https://github.com/login/oauth/access_token', + method: 'POST', + data: { + refresh_token: token.refreshToken, + grant_type: 'authorization_code', + client_secret: GITHUB_CLIENT_SECRET, + client_id: GITHUB_CLIENT_ID, + }, + }); + return data; + } + + revokeToken(token: TokenDocument): Promise<void> { + throw new Error('Method not implemented.'); + } + + private async getUser(accessToken: string) { + const { data } = await githubClient({ + url: '/user', + method: 'GET', + headers: { + Authorization: 'Bearer ' + accessToken, + }, + }); + return data; + } +} diff --git a/apps/auth/src/app/services/OAuthTwitchService.ts b/apps/auth/src/app/services/OAuthTwitchService.ts new file mode 100644 index 000000000..8787c9f32 --- /dev/null +++ b/apps/auth/src/app/services/OAuthTwitchService.ts @@ -0,0 +1,90 @@ +import { AUTH_URL, TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET } from '@thxnetwork/auth/config/secrets'; +import { AccessTokenKind, OAuthTwitchScope } from '@thxnetwork/common/enums/AccessTokenKind'; +import { twitchClient } from '../util/axios'; +import { IOAuthService } from './interfaces/IOAuthService'; +import { Token, TokenDocument } from '../models/Token'; + +export default class TwitchService implements IOAuthService { + getLoginURL({ uid, scopes }: { uid: string; scopes: OAuthTwitchScope[] }) { + const state = Buffer.from(JSON.stringify({ uid })).toString('base64'); + const url = new URL('https://id.twitch.tv/oauth2/authorize'); + url.searchParams.append('state', state); + url.searchParams.append('response_type', 'code'); + url.searchParams.append('force_verify', 'true'); + url.searchParams.append('client_id', TWITCH_CLIENT_ID); + url.searchParams.append('redirect_uri', AUTH_URL + '/oidc/callback/twitch'); + url.searchParams.append('scope', scopes.join(' ')); + + return url.toString(); + } + + async requestToken(code: string): Promise<Partial<TToken>> { + const body = new URLSearchParams(); + body.append('code', code); + body.append('grant_type', 'authorization_code'); + body.append('redirect_uri', AUTH_URL + '/oidc/callback/twitch'); + body.append('client_secret', TWITCH_CLIENT_SECRET); + body.append('client_id', TWITCH_CLIENT_ID); + + const { data } = await twitchClient({ + url: 'https://id.twitch.tv/oauth2/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: body, + }); + + const user = await this.getUser(data.access_token); + + return { + kind: AccessTokenKind.Twitch, + accessToken: data.access_token, + refreshToken: data.refresh_token, + expiry: Date.now() + Number(data.expires_in) * 1000, + userId: user.id, + }; + } + + async refreshToken(token: TokenDocument) { + const body = new URLSearchParams(); + body.append('grant_type', 'refresh_token'); + body.append('refresh_token', token.refreshToken); + body.append('client_secret', TWITCH_CLIENT_SECRET); + body.append('client_id', TWITCH_CLIENT_ID); + + const { data } = await twitchClient({ + url: 'https://id.twitch.tv/oauth2/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: body, + }); + + return await Token.findByIdAndUpdate( + token._id, + { + accessToken: data.access_token, + refreshToken: data.refresh_token, + expiry: Date.now() + Number(data.expires_in) * 1000, + }, + { new: true }, + ); + } + + revokeToken(token: TokenDocument): Promise<void> { + throw new Error('Method not implemented.'); + } + + private async getUser(accessToken: string) { + const { data } = await twitchClient({ + url: 'https://id.twitch.tv/oauth2/userinfo', + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + return data; + } +} diff --git a/apps/auth/src/app/services/OAuthTwitterService.ts b/apps/auth/src/app/services/OAuthTwitterService.ts new file mode 100644 index 000000000..6b6082b1b --- /dev/null +++ b/apps/auth/src/app/services/OAuthTwitterService.ts @@ -0,0 +1,122 @@ +import { URLSearchParams } from 'url'; +import { twitterClient } from '../util/axios'; +import { AUTH_URL, TWITTER_CLIENT_ID, TWITTER_CLIENT_SECRET } from '../config/secrets'; +import { AccessTokenKind, OAuthTwitterScope } from '@thxnetwork/common/enums/AccessTokenKind'; +import { IOAuthService } from '../services/interfaces/IOAuthService'; +import { Token, TokenDocument } from '../models/Token'; +import { logger } from '../util/logger'; + +export default class TwitterService implements IOAuthService { + getLoginURL({ uid, scopes }: { uid: string; scopes: OAuthTwitterScope[] }): string { + const state = Buffer.from(JSON.stringify({ uid })).toString('base64'); + const redirectURL = AUTH_URL + '/oidc/callback/twitter'; + const authorizeURL = new URL('https://twitter.com/i/oauth2/authorize'); + + authorizeURL.searchParams.append('response_type', 'code'); + authorizeURL.searchParams.append('client_id', TWITTER_CLIENT_ID); + authorizeURL.searchParams.append('redirect_uri', redirectURL); + authorizeURL.searchParams.append('scope', scopes.join(' ')); + authorizeURL.searchParams.append('state', state); + authorizeURL.searchParams.append('code_challenge', 'challenge'); + authorizeURL.searchParams.append('code_challenge_method', 'plain'); + + return authorizeURL.toString(); + } + + async requestToken(code: string): Promise<Partial<TToken>> { + const authHeader = 'Basic ' + Buffer.from(`${TWITTER_CLIENT_ID}:${TWITTER_CLIENT_SECRET}`).toString('base64'); + const body = new URLSearchParams(); + body.append('code', code); + body.append('grant_type', 'authorization_code'); + body.append('client_id', TWITTER_CLIENT_ID); + body.append('redirect_uri', AUTH_URL + '/oidc/callback/twitter'); + body.append('code_verifier', 'challenge'); + + const { data } = await twitterClient({ + url: '/oauth2/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': authHeader, + }, + data: body, + }); + const expiry = data.expires_in ? Date.now() + Number(data.expires_in) * 1000 : undefined; + const user = await this.getUser(data.access_token); + + return { + kind: AccessTokenKind.Twitter, + accessToken: data.access_token, + refreshToken: data.refresh_token, + expiry, + scopes: data.scope.split(' '), + userId: user.id, + metadata: { + name: user.name, + username: user.username, + }, + }; + } + + async refreshToken(token: TokenDocument) { + const authHeader = 'Basic ' + Buffer.from(`${TWITTER_CLIENT_ID}:${TWITTER_CLIENT_SECRET}`).toString('base64'); + const body = new URLSearchParams(); + body.append('refresh_token', token.refreshToken); + body.append('grant_type', 'refresh_token'); + body.append('client_id', TWITTER_CLIENT_ID); + + const { data } = await twitterClient({ + url: '/oauth2/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': authHeader, + }, + data: body, + }); + + return await Token.findByIdAndUpdate( + token._id, + { + accessToken: data.access_token, + refreshToken: data.refresh_token, + expiry: Date.now() + Number(data.expires_in) * 1000, + }, + { new: true }, + ); + } + + async revokeToken(token: TokenDocument): Promise<void> { + const body = new URLSearchParams(); + body.append('token', token.accessToken); + body.append('token_type_hint', 'access_token'); + body.append('client_id', TWITTER_CLIENT_ID); + try { + await twitterClient({ + url: '/oauth2/revoke', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: body, + }); + } catch (error) { + // Revocation request is failing with a 401, insufficient docs make it hard to + // gues what the exact payload should be, so for now we fail silently so the + // token can be removed from storage. + logger.error(error); + } + } + + private async getUser(accessToken: string) { + const { data } = await twitterClient({ + url: '/users/me', + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + return data.data; + } +} diff --git a/apps/auth/src/app/services/OAuthYouTubeService.ts b/apps/auth/src/app/services/OAuthYouTubeService.ts new file mode 100644 index 000000000..3bcf0e491 --- /dev/null +++ b/apps/auth/src/app/services/OAuthYouTubeService.ts @@ -0,0 +1,87 @@ +import axios from 'axios'; +import { google } from 'googleapis'; +import { AccessTokenKind, OAuthGoogleScope } from '@thxnetwork/common/enums/AccessTokenKind'; +import { AUTH_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '../config/secrets'; +import { parseJwt } from '../util/jwt'; +import { Token, TokenDocument } from '../models/Token'; +import { IOAuthService } from '../services/interfaces/IOAuthService'; +import { logger } from '../util/logger'; + +const client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, AUTH_URL + '/oidc/callback/google'); + +google.options({ auth: client }); + +export default class YouTubeService implements IOAuthService { + getLoginURL({ uid, scopes }: { uid: string; scopes: OAuthGoogleScope[] }) { + const state = Buffer.from(JSON.stringify({ uid })).toString('base64'); + return client.generateAuthUrl({ + state, + access_type: 'offline', + scope: scopes, + }); + } + + async requestToken(code: string) { + const { tokens } = await client.getToken(code); + const expiry = tokens.expiry_date ? Date.now() + Number(tokens.expiry_date) : undefined; + const claims = await parseJwt(tokens.id_token); + + return { + kind: AccessTokenKind.Google, + expiry, + scopes: tokens.scope.split(' ') as OAuthGoogleScope[], + accessToken: tokens.access_token, + refreshToken: tokens.refresh_token, + userId: claims.sub, + }; + } + + async refreshToken(token: TokenDocument): Promise<TokenDocument> { + const refreshToken = await this.getRefreshToken(token); + + client.setCredentials({ + refresh_token: refreshToken, + access_token: token.accessToken, + }); + + const googleToken = await client.getAccessToken(); + const { expiry_date, sub, scopes } = await client.getTokenInfo(googleToken.token); + + return await Token.findByIdAndUpdate( + token._id, + { + accessToken: googleToken, + refreshToken, + expiry: expiry_date, + scope: scopes.join(' '), + userId: sub, + }, + { new: true }, + ); + } + + async revokeToken(token: TokenDocument): Promise<void> { + try { + const url = new URL('https://oauth2.googleapis.com/revoke'); + if (token.accessToken) { + await axios({ url: url.toString(), method: 'POST', params: { token: token.accessToken } }); + } + if (token.refreshToken) { + await axios({ url: url.toString(), method: 'POST', params: { token: token.refreshToken } }); + } + } catch (error) { + logger.error(error); + } + } + + // We only get one refreshToken from google and should use it for + // all token variations we store + private async getRefreshToken({ sub }: TokenDocument) { + const [token] = await Token.find({ + scope: { $in: ['google', 'youtube-view', 'youtube-manage'] }, + refreshToken: { $exists: true, $ne: '' }, + sub, + }); + return token && token.refreshToken; + } +} diff --git a/apps/auth/src/app/services/TokenService.ts b/apps/auth/src/app/services/TokenService.ts new file mode 100644 index 000000000..2c568b73f --- /dev/null +++ b/apps/auth/src/app/services/TokenService.ts @@ -0,0 +1,112 @@ +import { AccessTokenKind, OAuthScope } from '@thxnetwork/common/enums'; +import { Token, TokenDocument } from '../models/Token'; +import { AccountDocument } from '../models/Account'; +import { decryptString } from '../util/decrypt'; +import { SECURE_KEY } from '../config/secrets'; +import { IOAuthService } from './interfaces/IOAuthService'; +import DiscordService from './OAuthDiscordService'; +import TwitterService from './OAuthTwitterService'; +import TwitchService from './OAuthTwitchService'; +import YouTubeService from './OAuthYouTubeService'; +import GithubService from './OAuthGithubService'; +import { logger } from '../util/logger'; + +const serviceMap: { [variant: string]: IOAuthService } = { + [AccessTokenKind.Twitter]: new TwitterService(), + [AccessTokenKind.Google]: new YouTubeService(), + [AccessTokenKind.Discord]: new DiscordService(), + [AccessTokenKind.Twitch]: new TwitchService(), + [AccessTokenKind.Github]: new GithubService(), +}; + +export default class TokenService { + static getLoginURL({ kind, uid, scopes }: { kind: AccessTokenKind; uid: string; scopes: OAuthScope[] }) { + return serviceMap[kind].getLoginURL({ uid, scopes }); + } + + static request({ kind, code }: { kind: AccessTokenKind; code: string }) { + return serviceMap[kind].requestToken(code); + } + + static revoke(token: TokenDocument) { + return serviceMap[token.kind].revokeToken(token); + } + + static async refresh(token: TokenDocument) { + // Return token if there is no expiry or no refreshtoken + if (!token || !token.expiry || !token.refreshToken) return token; + + // Check if token is expired + const isExpired = Date.now() > token.expiry; + if (!isExpired) return token; + + try { + // If so, refresh the token and return + return await serviceMap[token.kind].refreshToken(token); + } catch (error) { + logger.error(error); + logger.error('Token refresh failed'); + return token; + } + } + + static async getToken(account: AccountDocument, kind: AccessTokenKind): Promise<TokenDocument> { + const token = await Token.findOne({ sub: account._id, kind }); + if (!token) return; + + const { accessTokenEncrypted, refreshTokenEncrypted } = token; + const accessToken = accessTokenEncrypted && decryptString(accessTokenEncrypted, SECURE_KEY); + const refreshToken = refreshTokenEncrypted && decryptString(refreshTokenEncrypted, SECURE_KEY); + const refreshedToken = await this.refresh(token); + + return { ...refreshedToken.toJSON(), accessToken, refreshToken }; + } + + static async connect(account: AccountDocument, token: Partial<TokenDocument>) { + // Check if any other accounts are using this token + const tokens = await Token.find({ kind: token.kind, userId: token.userId, sub: { $ne: String(account._id) } }); + if (tokens.length) { + throw new Error('Already connect to another THX account! Please disconnect that account first.'); + } + + // Check if this account already has a token but with another userId + const existingToken = await Token.findOne({ sub: String(account._id), kind: token.kind }); + if (existingToken && existingToken.userId !== token.userId) { + throw new Error( + 'Already connected to a different account from this provider! Please disconnect that account first.', + ); + } + + // Store the token for the account + return await this.setToken(account, token); + } + + static async setToken(account: AccountDocument, token: Partial<TokenDocument>) { + // Store the token for the new account + return Token.findOneAndUpdate( + { sub: account._id, kind: token.kind }, + { ...token, sub: account._id }, + { upsert: true, new: true }, + ); + } + + static async unsetToken(account: AccountDocument, kind: AccessTokenKind) { + const token = await this.getToken(account, kind); + + // Revoke access at token provider if token has scopes + if (token.scopes.length) { + await this.revoke(token); + } + + // Remove from storage + return this.remove({ sub: account._id, kind }); + } + + static remove({ sub, kind }: { sub: string; kind: AccessTokenKind }) { + return Token.findOneAndDelete({ sub, kind }); + } + + static findTokenForUserId(userId: string, kind: AccessTokenKind) { + return Token.findOne({ userId, kind }); + } +} diff --git a/apps/auth/src/app/services/interfaces/IOAuthService.ts b/apps/auth/src/app/services/interfaces/IOAuthService.ts new file mode 100644 index 000000000..eae3538e9 --- /dev/null +++ b/apps/auth/src/app/services/interfaces/IOAuthService.ts @@ -0,0 +1,9 @@ +import { OAuthScope } from '@thxnetwork/common/enums'; +import { TokenDocument } from '../../models/Token'; + +export interface IOAuthService { + getLoginURL(options: { uid: string; scopes: OAuthScope[] }): string; + requestToken(code: string): Promise<Partial<TokenDocument>>; + refreshToken(token: TokenDocument): Promise<TokenDocument>; + revokeToken(token: TokenDocument): Promise<void>; +} diff --git a/apps/auth/src/app/types/CommonOauthLoginOptions.ts b/apps/auth/src/app/types/CommonOauthLoginOptions.ts new file mode 100644 index 000000000..018f5be92 --- /dev/null +++ b/apps/auth/src/app/types/CommonOauthLoginOptions.ts @@ -0,0 +1,6 @@ +type CommonOauthLoginOptions = Partial<{ + scope?: string[]; + redirectUrl?: string; +}>; + +export default CommonOauthLoginOptions; diff --git a/apps/auth/src/app/types/definitions/augment-express-request.d.ts b/apps/auth/src/app/types/definitions/augment-express-request.d.ts new file mode 100644 index 000000000..d107d8f1d --- /dev/null +++ b/apps/auth/src/app/types/definitions/augment-express-request.d.ts @@ -0,0 +1,11 @@ +export {}; + +declare global { + namespace Express { + interface Request { + origin?: string; + auth?: any; + interaction?: any; + } + } +} diff --git a/apps/auth/src/app/types/definitions/cors.d.ts b/apps/auth/src/app/types/definitions/cors.d.ts new file mode 100644 index 000000000..dff30ae18 --- /dev/null +++ b/apps/auth/src/app/types/definitions/cors.d.ts @@ -0,0 +1,2 @@ +declare module 'cors'; +declare module 'host-validation'; diff --git a/apps/auth/src/app/types/definitions/jsonwebtoken.d.ts b/apps/auth/src/app/types/definitions/jsonwebtoken.d.ts new file mode 100644 index 000000000..0493c0083 --- /dev/null +++ b/apps/auth/src/app/types/definitions/jsonwebtoken.d.ts @@ -0,0 +1 @@ +declare module 'jsonwebtoken'; diff --git a/apps/auth/src/app/types/definitions/morgan-json.d.ts b/apps/auth/src/app/types/definitions/morgan-json.d.ts new file mode 100644 index 000000000..2b861c8c3 --- /dev/null +++ b/apps/auth/src/app/types/definitions/morgan-json.d.ts @@ -0,0 +1 @@ +declare module 'morgan-json'; diff --git a/apps/auth/src/app/types/definitions/morgan.d.ts b/apps/auth/src/app/types/definitions/morgan.d.ts new file mode 100644 index 000000000..0b6637ede --- /dev/null +++ b/apps/auth/src/app/types/definitions/morgan.d.ts @@ -0,0 +1 @@ +declare module 'morgan'; diff --git a/apps/auth/src/app/types/definitions/qrcode.d.ts b/apps/auth/src/app/types/definitions/qrcode.d.ts new file mode 100644 index 000000000..c9f8a23a7 --- /dev/null +++ b/apps/auth/src/app/types/definitions/qrcode.d.ts @@ -0,0 +1 @@ +declare module 'qrcode'; diff --git a/apps/auth/src/app/types/definitions/swagger-ui-express.d.ts b/apps/auth/src/app/types/definitions/swagger-ui-express.d.ts new file mode 100644 index 000000000..d7910fe9e --- /dev/null +++ b/apps/auth/src/app/types/definitions/swagger-ui-express.d.ts @@ -0,0 +1 @@ +declare module 'swagger-ui-express'; diff --git a/apps/auth/src/app/types/enums/chainId.ts b/apps/auth/src/app/types/enums/chainId.ts new file mode 100644 index 000000000..1d52711a1 --- /dev/null +++ b/apps/auth/src/app/types/enums/chainId.ts @@ -0,0 +1,6 @@ +export enum ChainId { + Hardhat = 31337, + PolygonMumbai = 80001, + Polygon = 137, + PolygonZK = 1101, +} diff --git a/apps/auth/src/app/types/index.ts b/apps/auth/src/app/types/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/apps/auth/src/app/util/adapter.ts b/apps/auth/src/app/util/adapter.ts new file mode 100644 index 000000000..d1bb40bca --- /dev/null +++ b/apps/auth/src/app/util/adapter.ts @@ -0,0 +1,106 @@ +import { snakeCase } from 'lodash'; +import db from './database'; + +const grantable = new Set(['access_token', 'authorization_code', 'refresh_token', 'device_code']); + +let DB: any; + +db.connection.once('open', async () => { + DB = db.connection.getClient().db(); +}); + +class CollectionSet extends Set { + add(name: string): any { + const nu = this.has(name); + super.add(name); + if (!nu) { + DB.collection(name) + .createIndexes([ + ...(grantable.has(name) + ? [ + { + key: { 'payload.grantId': 1 }, + }, + ] + : []), + ...(name === 'device_code' + ? [ + { + key: { 'payload.userCode': 1 }, + unique: true, + }, + ] + : []), + ...(name === 'session' + ? [ + { + key: { 'payload.uid': 1 }, + unique: true, + }, + ] + : []), + { + key: { expiresAt: 1 }, + expireAfterSeconds: 0, + }, + ]) + .catch(console.error); // eslint-disable-line no-console + } + } +} + +const collections = new CollectionSet(); + +export default class MongoAdapter { + name: string; + + constructor(name: string) { + this.name = snakeCase(name); + + collections.add(this.name); + } + + coll() { + return DB.collection(this.name); + } + + async upsert(_id: string, payload: any, expiresIn: number) { + const expiresAt = expiresIn ? new Date(Date.now() + expiresIn * 1000) : null; + await this.coll().updateOne( + { _id }, + { $set: { payload, ...(expiresAt ? { expiresAt } : undefined) } }, + { upsert: true }, + ); + } + + async find(_id: string) { + const result = await this.coll().find({ _id }, { payload: 1 }).limit(1).next(); + if (!result) return undefined; + return result.payload; + } + async findByUserCode(userCode: string) { + const result = await this.coll().find({ 'payload.userCode': userCode }, { payload: 1 }).limit(1).next(); + + if (!result) return undefined; + return result.payload; + } + + async findByUid(uid: string) { + const result = await this.coll().find({ 'payload.uid': uid }, { payload: 1 }).limit(1).next(); + + if (!result) return undefined; + return result.payload; + } + + async destroy(_id: string) { + await this.coll().deleteOne({ _id }); + } + + async revokeByGrantId(grantId: string) { + await this.coll().deleteMany({ 'payload.grantId': grantId }); + } + + async consume(_id: string) { + await this.coll().findOneAndUpdate({ _id }, { $set: { 'payload.consumed': Math.floor(Date.now() / 1000) } }); + } +} diff --git a/apps/auth/src/app/util/api.ts b/apps/auth/src/app/util/api.ts new file mode 100644 index 000000000..e88119b52 --- /dev/null +++ b/apps/auth/src/app/util/api.ts @@ -0,0 +1,50 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { URLSearchParams } from 'url'; + +import { THXError } from './errors'; +import { API_URL, AUTH_CLIENT_ID, AUTH_CLIENT_SECRET, AUTH_URL } from '../config/secrets'; + +class ApiAccesTokenRequestError extends THXError { + message = 'API access token request failed'; +} + +let apiAccessToken = ''; +let apiAccessTokenExpired = 0; + +async function requestAuthAccessToken() { + const data = new URLSearchParams(); + data.append('grant_type', 'client_credentials'); + data.append('resource', API_URL); + data.append('scope', 'openid brands:read claims:read wallets:read wallets:write pools:write pools:read'); + const r = await axios({ + baseURL: AUTH_URL, + url: '/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + Buffer.from(`${AUTH_CLIENT_ID}:${AUTH_CLIENT_SECRET}`).toString('base64'), + }, + data, + }); + + if (r.status !== 200) throw new ApiAccesTokenRequestError(); + return r.data; +} + +export async function getAuthAccessToken() { + if (Date.now() > apiAccessTokenExpired) { + const { access_token, expires_in } = await requestAuthAccessToken(); + apiAccessToken = access_token; + apiAccessTokenExpired = Date.now() + expires_in * 1000; + } + + return `Bearer ${apiAccessToken}`; +} + +export async function apiClient(config: AxiosRequestConfig) { + const authHeader = await getAuthAccessToken(); + if (!config.headers) config.headers = {}; + config.headers['Authorization'] = authHeader; + config.baseURL = API_URL; + return axios(config); +} diff --git a/apps/auth/src/app/util/axios.ts b/apps/auth/src/app/util/axios.ts new file mode 100644 index 000000000..5a240d9cb --- /dev/null +++ b/apps/auth/src/app/util/axios.ts @@ -0,0 +1,43 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { + DISCORD_API_ENDPOINT, + GITHUB_API_ENDPOINT, + TWITCH_API_ENDPOINT, + TWITTER_API_ENDPOINT, +} from '../config/secrets'; + +export async function twitterClient(config: AxiosRequestConfig) { + try { + const client = axios.create({ ...config, baseURL: TWITTER_API_ENDPOINT }); + return await client(config); + } catch (error) { + throw error.response; + } +} + +export async function discordClient(config: AxiosRequestConfig) { + try { + const client = axios.create({ ...config, baseURL: DISCORD_API_ENDPOINT }); + return await client(config); + } catch (error) { + throw error.response; + } +} + +export async function githubClient(config: AxiosRequestConfig) { + try { + const client = axios.create({ ...config, baseURL: GITHUB_API_ENDPOINT }); + return await client(config); + } catch (error) { + throw error.response; + } +} + +export async function twitchClient(config: AxiosRequestConfig) { + try { + const client = axios.create({ ...config, baseURL: TWITCH_API_ENDPOINT }); + return await client(config); + } catch (error) { + throw error.response; + } +} diff --git a/apps/auth/src/app/util/database.ts b/apps/auth/src/app/util/database.ts new file mode 100644 index 000000000..a725f0bf2 --- /dev/null +++ b/apps/auth/src/app/util/database.ts @@ -0,0 +1,53 @@ +import mongoose from 'mongoose'; +import bluebird from 'bluebird'; +import { logger } from './logger'; + +(mongoose as any).Promise = bluebird; + +const connect = async (url: string) => { + mongoose.connection.on('error', (err) => { + logger.error(`MongoDB connection error. Please make sure MongoDB is running. ${err}`); + }); + + mongoose.connection.on('reconnectFailed', () => { + logger.error('Unable to recoonect to MongoDB'); + process.exit(); + }); + + mongoose.connection.on('open', () => { + logger.info(`MongoDB successfully connected to ${url.split('@')[1]}`); + }); + + if (mongoose.connection.readyState === 0) { + await mongoose.connect(url); + } +}; + +const truncate = async () => { + if (mongoose.connection.readyState !== 0) { + const { collections } = mongoose.connection; + const promises = Object.keys(collections).map((collection) => + mongoose.connection.collection(collection).deleteMany({}), + ); + + await Promise.all(promises); + } +}; + +const readyState = () => { + return mongoose.connection.readyState; +}; + +const disconnect = async () => { + if (mongoose.connection.readyState !== 0) { + await mongoose.disconnect(); + } +}; + +export default { + connect, + truncate, + disconnect, + readyState, + connection: mongoose.connection, +}; diff --git a/apps/auth/src/app/util/decrypt.ts b/apps/auth/src/app/util/decrypt.ts new file mode 100644 index 000000000..021c34368 --- /dev/null +++ b/apps/auth/src/app/util/decrypt.ts @@ -0,0 +1,44 @@ +import crypto from 'crypto'; + +const ALGORITHM_NAME = 'aes-128-gcm'; +const ALGORITHM_NONCE_SIZE = 12; +const ALGORITHM_TAG_SIZE = 16; +const ALGORITHM_KEY_SIZE = 16; +const PBKDF2_NAME = 'sha256'; +const PBKDF2_SALT_SIZE = 16; +const PBKDF2_ITERATIONS = 1000; + +function decrypt(ciphertextAndNonce: any, key: any) { + // Create buffers of nonce, ciphertext and tag. + const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE); + const ciphertext = ciphertextAndNonce.slice(ALGORITHM_NONCE_SIZE, ciphertextAndNonce.length - ALGORITHM_TAG_SIZE); + const tag = ciphertextAndNonce.slice(ciphertext.length + ALGORITHM_NONCE_SIZE); + + // Create the cipher instance. + const cipher = crypto.createDecipheriv(ALGORITHM_NAME, key, nonce); + + // Decrypt and return result. + cipher.setAuthTag(tag); + return Buffer.concat([cipher.update(ciphertext), cipher.final()]); +} + +export function decryptString(base64CiphertextAndNonceAndSalt: any, password: any) { + // Decode the base64. + const ciphertextAndNonceAndSalt = Buffer.from(base64CiphertextAndNonceAndSalt, 'base64'); + + // Create buffers of salt and ciphertextAndNonce. + const salt = ciphertextAndNonceAndSalt.slice(0, PBKDF2_SALT_SIZE); + const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(PBKDF2_SALT_SIZE); + + // Derive the key using PBKDF2. + const key = crypto.pbkdf2Sync( + Buffer.from(password, 'utf8'), + salt, + PBKDF2_ITERATIONS, + ALGORITHM_KEY_SIZE, + PBKDF2_NAME, + ); + + // Decrypt and return result. + return decrypt(ciphertextAndNonce, key).toString('utf8'); +} diff --git a/apps/auth/src/app/util/encrypt.ts b/apps/auth/src/app/util/encrypt.ts new file mode 100644 index 000000000..831b470c0 --- /dev/null +++ b/apps/auth/src/app/util/encrypt.ts @@ -0,0 +1,42 @@ +// https://github.com/luke-park/SecureCompatibleEncryptionExamples/blob/master/JavaScript/SCEE-Node.js +import crypto from 'crypto'; + +const ALGORITHM_NAME = 'aes-128-gcm'; +const ALGORITHM_NONCE_SIZE = 12; +const ALGORITHM_KEY_SIZE = 16; +const PBKDF2_NAME = 'sha256'; +const PBKDF2_SALT_SIZE = 16; +const PBKDF2_ITERATIONS = 1000; + +function encrypt(plaintext: any, key: any) { + // Generate a 96-bit nonce using a CSPRNG. + const nonce = crypto.randomBytes(ALGORITHM_NONCE_SIZE); + + // Create the cipher instance. + const cipher = crypto.createCipheriv(ALGORITHM_NAME, key, nonce); + + // Encrypt and prepend nonce. + const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); + + return Buffer.concat([nonce, ciphertext, cipher.getAuthTag()]); +} + +export function encryptString(plaintext: string, password: string) { + // Generate a 128-bit salt using a CSPRNG. + const salt = crypto.randomBytes(PBKDF2_SALT_SIZE); + + // Derive a key using PBKDF2. + const key = crypto.pbkdf2Sync( + Buffer.from(password, 'utf8'), + salt, + PBKDF2_ITERATIONS, + ALGORITHM_KEY_SIZE, + PBKDF2_NAME, + ); + + // Encrypt and prepend salt. + const ciphertextAndNonceAndSalt = Buffer.concat([salt, encrypt(Buffer.from(plaintext, 'utf8'), key)]); + + // Return as base64 string. + return ciphertextAndNonceAndSalt.toString('base64'); +} diff --git a/apps/auth/src/app/util/errors.ts b/apps/auth/src/app/util/errors.ts new file mode 100644 index 000000000..c2c8a69a2 --- /dev/null +++ b/apps/auth/src/app/util/errors.ts @@ -0,0 +1,88 @@ +class THXError extends Error { + message: string; + + constructor(message?: string) { + super(message); + this.name = this.constructor.name; + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + } +} + +class THXHttpError extends THXError { + status: number; + constructor(message?: string, status?: number) { + super(message); + if (status) { + this.status = status; + } + } +} + +class BadRequestError extends THXHttpError { + status = 400; + constructor(message?: string) { + super(message || 'Bad Request'); + } +} + +class UnauthorizedError extends THXHttpError { + status = 401; + constructor(message?: string) { + super(message || 'Unauthorized'); + } +} + +class ForbiddenError extends THXHttpError { + status = 403; + constructor(message?: string) { + super(message || 'Forbidden'); + } +} + +class NotFoundError extends THXHttpError { + status = 404; + constructor(message?: string) { + super(message || 'Not Found'); + } +} + +class UnprocessableEntityError extends THXHttpError { + status = 422; + constructor(message?: string) { + super(message || 'Unprocessable Entity'); + } +} + +class InternalServerError extends THXHttpError { + status = 500; + constructor(message?: string) { + super(message || 'Internal Server Error'); + } +} + +class NotImplementedError extends THXHttpError { + status = 501; + constructor(message?: string) { + super(message || 'Not Implemented'); + } +} + +class BadGatewayError extends THXHttpError { + status = 502; + constructor(message?: string) { + super(message || 'Bad Gateway'); + } +} + +export { + THXError, + THXHttpError, + BadRequestError, + UnauthorizedError, + ForbiddenError, + NotFoundError, + UnprocessableEntityError, + NotImplementedError, + BadGatewayError, + InternalServerError, +}; diff --git a/apps/auth/src/app/util/healthcheck.ts b/apps/auth/src/app/util/healthcheck.ts new file mode 100644 index 000000000..5c1446e0c --- /dev/null +++ b/apps/auth/src/app/util/healthcheck.ts @@ -0,0 +1,39 @@ +import newrelic from 'newrelic'; +import { HealthCheck } from '@godaddy/terminus'; +import { config, status } from 'migrate-mongo'; +import { connection } from 'mongoose'; + +import migrateMongoConfig from '../config/migrate-mongo'; + +const dbConnected = async () => { + // https://mongoosejs.com/docs/api.html#connection_Connection-readyState + const { readyState } = connection; + // ERR_CONNECTING_TO_MONGO + if (readyState === 0 || readyState === 3) { + throw new Error('Mongoose has disconnected'); + } + // CONNECTING_TO_MONGO + if (readyState === 2) { + throw new Error('Mongoose is connecting'); + } + // CONNECTED_TO_MONGO + return; +}; + +const migrationsApplied = async () => { + config.set(migrateMongoConfig); + + const pendingMigrations = (await status(connection.db as any)).filter( + (migration) => migration.appliedAt === 'PENDING', + ); + if (pendingMigrations.length > 0) { + throw new Error('Not all migrations applied'); + } + + return; +}; + +export const healthCheck: HealthCheck = () => { + newrelic.getTransaction().ignore(); + return Promise.all([dbConnected(), migrationsApplied()]); +}; diff --git a/apps/auth/src/app/util/helmet.ts b/apps/auth/src/app/util/helmet.ts new file mode 100644 index 000000000..76aca8617 --- /dev/null +++ b/apps/auth/src/app/util/helmet.ts @@ -0,0 +1,41 @@ +import helmet from 'helmet'; +// import { AUTH_URL, DASHBOARD_URL, WALLET_URL } from '@thxnetwork/auth/config/secrets'; + +export const helmetInstance = helmet({ + // contentSecurityPolicy: { + // directives: { + // defaultSrc: [AUTH_URL, "'unsafe-eval'", "'unsafe-inline'"], + // frameSrc: [AUTH_URL, WALLET_URL, DASHBOARD_URL], + // frameAncestors: [WALLET_URL, DASHBOARD_URL], + // fontSrc: ['https://fonts.gstatic.com', 'https://ka-f.fontawesome.com/'], + // connectSrc: ['https://ka-f.fontawesome.com'], + // scriptSrcElem: [ + // AUTH_URL, + // 'https://www.googletagmanager.com', + // 'https://kit.fontawesome.com', + // 'https://cdn.jsdelivr.net', + // 'https://unpkg.com/', + // 'https://cdnjs.cloudflare.com', + // "'sha256-PEI/gdNohg23HbZboqauC7uLjfrpcON9Z4W9IurYRxk='", + // "'sha256-jOpZSqrqP85EQ9xzce9PQ0EFR3DhpJcbc+vVR1OQLHQ='", + // "'sha256-5+pexDB9ERu/BGJRRg/9bZuuZwHoAYgdA9L6UuOaIPY='", + // "'sha256-tHn9v4E9xZmG7Eh4CSF7CHyPU7kSwiu32J8PimHwftU='", + // ], + // styleSrcElem: [ + // AUTH_URL, + // 'https://fonts.googleapis.com', + // 'https://ka-f.fontawesome.com', + // "'sha256-uCITVBkyNmwuSQXzSNUuRx7G7+1kS2zWJ9SjHF0W2QA='", + // "'sha256-bepHRYpM181zEsx4ClPGLgyLPMyNCxPBrA6m49/Ozqg='", + // "'sha256-ZL58hL5KbUHBRnMK797rN7IR+Tg9Aw61ddJ/rmxn1KM='", + // "'sha256-75mE4wfpMmhCBnDZSF3PLGDQFzUteIHYrgFoOGlCMQw='", + // ], + // }, + // }, + contentSecurityPolicy: false, + hidePoweredBy: true, + frameguard: false, + referrerPolicy: { + policy: ['origin'], + }, +}); diff --git a/apps/auth/src/app/util/jest/constants.ts b/apps/auth/src/app/util/jest/constants.ts new file mode 100644 index 000000000..c53523f68 --- /dev/null +++ b/apps/auth/src/app/util/jest/constants.ts @@ -0,0 +1,91 @@ +import jwt from 'jsonwebtoken'; +import { AUTH_URL } from '../../config/secrets'; + +const privateKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAwaZ3afW0/zYy3HfJwAAr83PDdZvADuSJ6jTZk1+jprdHdG6P +zH9XaB6xhzvwTIJFcWuREkNSC06MDLCuvmZ8fj93FcNaZ2ZJ0LFvY4SODMDqFekE +5vD2Y15aSI2Y69qwKlVLphvEEXJ/FRqIHQX9wwCtwVsnqcLt/f5aNWRHyk2jwhz7 +IBm+dLu9/CV8AsvE5ddgOYYbNk+SMCjznESZcMg1KRzbdawnOklzloc+Q0iCxQK7 +022ukVxFbmT7U1hTVOTOzrruqBxptPDiutkKfOXebzYyZodlFFL5MWcatCWS3XL5 +1KBIeKWny5mExZPzIf1ofGuJe0zxllw8olgMqQIDAQABAoIBAB6x2DO/cpURbjZr +9lqsrErGirDVoze5GfM5tVMa0cHXQ0g9TiXH+X7TfqhE4+38qC02M6SFbzfDl4db +ahdb/1ezj5ivgmDpYcHmnhVUKX/0BCa87L3+a8+MYRsm9ppL66iKJJeLxyRM1b/u +mKyhCnwiW2hOnpbWAwtDieD0qDx0kzkYLevG3MivaAe1rDD6gS2LhPy6tGtmemRv +uT03a1X9DEO0U45oDvhi0V6dm8jz/eBBybHt0WWNNi2c5PVjhIL3HfT2UMiNa0xf +O3g1eGc8bdVPW4z0Kz1g+H/uvguTEqKVq4yT8Y2LuF0Vm2MxptqAmxxJTyL5q0ZI +V7PDg0ECgYEAziWa0BWbX+cMfIJXNi89aMpgBqBLdvQt9rE/TGjVeViPBcG8AoRr +Osxzct6tMd+DQO3uzx5DES2FiM7gbOtpGKnLWjJImmCq2gPCtWXo74I8il5egJNh +vq03eP7wdcm+bFta4+ScGv2wAbx28y3+5fQZPqzUISz+2dM8Hxx9GO0CgYEA8Hs0 +ap+FKD/nbRFfYMVLeP0WfJYuSpn9MF8FT73RSvCva4Ql0a3WbaZ5AMgLFGjm5nRp +idf5vPMaXbleDG1xZnkhhBXidwFmn4TCVvG/fiDjXOULuJ5qjKLv+5dTG72GDHGG +onNUwn1LQ3bJpGZ3VHIFJXOcQHl2Dxa6Cn7G9y0CgYBn0gyL67XasNRbCJG/mj8F +PZbq/2PCPuu/KDlG1C1e9bjiH1X+to4CiOFD4t27FmRWGP6ClS0Vw6VS502jzVOa +tjjR7i0egrzJG8e979tGdILk9O4HNzKtAzPC3jJgQACFNeUqjQIJneY8mZwWkP2k +9jCYnhYftzeKoJXQ3VoraQKBgQDiprxYYdDWhqRQH7eNNWZUufSfp8wpc8k19djD +t1uzDfXHl90tKnKXFfelzOTkb5pwSffOe0hd1aJcA4GopN3kfvYfz6CKGT/nyPCB +kYeyEL05qIbLkkNKGaelsJIb6xyUTctfAOQ6Cm0NQL/7urdtV6mSCsyR1+h1gC4I +BkTwYQKBgC7s9J9rRT23bx0NKyyHKu7/akAdC8m0YCohU5L7NNw0UZql0p8EQguh +bKhNO4+rlwh2VzIi5tVMQTYoUbaab8n17fdNxtfTsG0h4vj8q+7ab59GYf1TKn0R +JEn/NS0gRKfNh6bwZaSTfhFxALmKApVNTPm2UT9G5hADTcw4xTQf +-----END RSA PRIVATE KEY-----`; + +export const jwksResponse = { + keys: [ + { + kty: 'RSA', + use: 'sig', + kid: 'qSjSTujaClXGGkwW4zdC1zpRHxAq099krc8TTFfXYlg', + alg: 'RS256', + e: 'AQAB', + n: 'oGGYqftZswJRU_vfKDi7jFNgH20LOVfOgSYBu39GEvYWeygBTGuHmf2Ict7m70IKiFAY24kOhMnNjFJ6B0dxdBt5Hf3Kw-VoTKp1MDuy6_wao51WbR5G6II7ND3_2jucyNLeHpYwKHmk00yo4XTBlihNB_ogLCsblupHFceULfq5DN1whTGp-ZHTdYZaSZGXBC_v046vzqIqdKF785AzN56fBDlWhJ0CB9PdLSqj5hd91mxIbJqarHHWR844R5gOLE5ZMJMwX7SUgirFXhDo_VPVKJsP06SxGWBHe8HaYYi6DSXA7i59yBWyJy80F4t3OBTX-AWkzSfJ-_O9zPEBXQ', + }, + ], +}; + +export function getToken(scope: string, outerSub?: string) { + const payload: any = { + scope, + }; + + if (scope === dashboardScopes) { + payload.sub = sub; + } else if (scope === walletScopes) { + payload.sub = sub2; + } + + if (outerSub) { + payload.sub = outerSub; + } + + const options = { + header: { kid: '0' }, + algorithm: 'RS256', + expiresIn: '1d', + issuer: AUTH_URL, + }; + + let token; + try { + token = jwt.sign(payload, privateKey, options); + } catch (err) { + console.log(err); + throw err; + } + + return `Bearer ${token}`; +} + +export const accountAddress = '0x287aAa0f0089069A115AF9D25f0adeB295b52964'; +export const accountEmail = 'test@test.com'; +export const accountSecret = 'mellon'; +export const clientId = 'xxxxxxx'; +export const sub = '6074cbdd1459355fae4b6a14'; +export const sub2 = '6074cbdd1459355fae4b6a15'; + +export const dashboardScopes = + 'openid pools:read pools:write erc20:write erc20:read erc721:write erc721:read rewards:read rewards:write deposits:read deposits:write promotions:read promotions:write widgets:write widgets:read transactions:read swaprule:read swaprule:write claims:read'; +export const dashboardAccessToken = getToken(dashboardScopes); +export const walletScopes = + 'openid rewards:read erc20:read erc721:read withdrawals:read withdrawals:write deposits:read deposits:write account:read account:write memberships:read memberships:write promotions:read payments:write payments:read relay:write transactions:read transactions:write swap:read swap:write swaprule:read claims:read wallets:read wallets:write'; +export const walletAccessToken = getToken(walletScopes); +export const walletAccessToken2 = getToken(walletScopes, sub); diff --git a/apps/auth/src/app/util/jest/index.ts b/apps/auth/src/app/util/jest/index.ts new file mode 100644 index 000000000..3f51aca95 --- /dev/null +++ b/apps/auth/src/app/util/jest/index.ts @@ -0,0 +1,2 @@ +export * from './utils'; +export * from './constants'; diff --git a/apps/auth/src/app/util/jest/mock.ts b/apps/auth/src/app/util/jest/mock.ts new file mode 100644 index 000000000..3e2bb3b79 --- /dev/null +++ b/apps/auth/src/app/util/jest/mock.ts @@ -0,0 +1,50 @@ +import nock from 'nock'; +import { getToken, jwksResponse } from './constants'; +import { API_URL, AUTH_URL } from '../../config/secrets'; + +export function mockAuthPath(method: string, path: string, status: number, callback: any = {}) { + console.log(AUTH_URL); + const n = nock(AUTH_URL).persist() as any; + return n[method](path).reply(status, callback); +} + +export function mockApiPath(method: string, path: string, status: number, callback: any = {}, query?: any) { + const n = nock(API_URL).persist() as any; + + const interceptor = n[method](path); + if (query) { + interceptor.query(query); + } + return interceptor.reply(status, callback); +} + +export function mockUrl(method: string, baseUrl: string, path: string, status: number, callback: any = {}) { + const n = nock(baseUrl).persist() as any; + return n[method](path).reply(status, callback); +} + +export const mockWalletProxy = () => { + const token = getToken('openid account:read account:write'); + mockAuthPath('get', '/jwks', 200, jwksResponse); + mockAuthPath('post', '/token', 200, async () => { + return { access_token: token }; + }); + + mockApiPath( + 'get', + `/v1/wallets`, + 200, + async () => { + return []; + }, + true, // mocks the entire url regardless of the passed query string: + ); + + mockApiPath('post', `/v1/wallets`, 200, async () => { + return true; + }); +}; + +export function mockClear() { + return nock.cleanAll(); +} diff --git a/apps/auth/src/app/util/jest/setup.ts b/apps/auth/src/app/util/jest/setup.ts new file mode 100644 index 000000000..e69de29bb diff --git a/apps/auth/src/app/util/jest/utils.ts b/apps/auth/src/app/util/jest/utils.ts new file mode 100644 index 000000000..be49d33e0 --- /dev/null +++ b/apps/auth/src/app/util/jest/utils.ts @@ -0,0 +1,3 @@ +export function getPath(url: string) { + return '/' + url.split('/')[3] + '/' + url.split('/')[4]; +} diff --git a/apps/auth/src/app/util/jwks.ts b/apps/auth/src/app/util/jwks.ts new file mode 100644 index 000000000..c585d4cec --- /dev/null +++ b/apps/auth/src/app/util/jwks.ts @@ -0,0 +1,5 @@ +import { JWKS_JSON } from '../config/secrets'; + +export function getJwks() { + if (JWKS_JSON) return JSON.parse(JWKS_JSON); +} diff --git a/apps/auth/src/app/util/jwt.ts b/apps/auth/src/app/util/jwt.ts new file mode 100644 index 000000000..38f44c647 --- /dev/null +++ b/apps/auth/src/app/util/jwt.ts @@ -0,0 +1,15 @@ +export function parseJwt(token: string) { + const base64Url = token.split('.')[1]; + const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + const jsonPayload = decodeURIComponent( + Buffer.from(base64, 'base64') + .toString('binary') + .split('') + .map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join(''), + ); + + return JSON.parse(jsonPayload); +} diff --git a/apps/auth/src/app/util/logger.ts b/apps/auth/src/app/util/logger.ts new file mode 100644 index 000000000..de9f449e0 --- /dev/null +++ b/apps/auth/src/app/util/logger.ts @@ -0,0 +1,14 @@ +import winston from 'winston'; +import { NODE_ENV } from '../config/secrets'; + +const formatWinston = winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + winston.format.errors({ stack: true }), + winston.format.json(), +); + +export const logger = winston.createLogger({ + level: NODE_ENV === 'test' ? 'warn' : 'debug', + format: formatWinston, + transports: [new winston.transports.Console()], +}); diff --git a/apps/auth/src/app/util/messages.ts b/apps/auth/src/app/util/messages.ts new file mode 100644 index 000000000..017257d8b --- /dev/null +++ b/apps/auth/src/app/util/messages.ts @@ -0,0 +1,14 @@ +export const ERROR_ACCOUNT_NOT_ACTIVE = 'Your e-mail is not verified. We have re-sent the activation link.'; +// export const ERROR_NO_ACCOUNT = 'We could not find an account for this e-mail and password combination.'; +export const ERROR_AUTH_LINK = 'Your wallet is encrypted by another party. Please ask them to send you a login link.'; +export const ERROR_OTP_CODE_INVALID = 'Could not validate your MFA code.'; +export const ERROR_AUTHENTICATION_TOKEN_INVALID_OR_EXPIRED = 'Your authentication token is invalid or expired.'; +export const ERROR_PASSWORD_NOT_MATCHING = 'Your provided passwords do not match'; +export const ERROR_SIGNUP_TOKEN_INVALID = 'Could not find an account for this signup token.'; +export const ERROR_SIGNUP_TOKEN_EXPIRED = 'This signup_token has expired.'; +export const SUCCESS_SIGNUP_COMPLETED = 'Congratulations! Your e-mail address has been verified.'; +export const ERROR_NO_ACCOUNT = 'Could not find an account for this address'; +export const ERROR_PASSWORD_RESET_TOKEN_INVALID_OR_EXPIRED = 'Your password reset token is invalid or expired.'; +export const ERROR_PASSWORD_STRENGTH = 'Please enter a strong password.'; +export const ERROR_VERIFY_EMAIL_TOKEN_INVALID = 'Could not find an account for this verify_email_token.'; +export const ERROR_VERIFY_EMAIL_EXPIRED = 'This verify_email_token has expired.'; diff --git a/apps/auth/src/app/util/mixpanel.ts b/apps/auth/src/app/util/mixpanel.ts new file mode 100644 index 000000000..97af80f54 --- /dev/null +++ b/apps/auth/src/app/util/mixpanel.ts @@ -0,0 +1,19 @@ +import mixpanel from 'mixpanel-browser'; +import { MIXPANEL_TOKEN } from '../config/secrets'; + +export const mixpanelClient = () => { + mixpanel.init(MIXPANEL_TOKEN); + return mixpanel; +}; + +export const track = { + UserVisits: (distinctId: string, path: string, params: string[]) => { + if (!MIXPANEL_TOKEN) return; + mixpanelClient().track(`user visits ${path}`, { distinct_id: distinctId, params }); + }, + UserCreates: (distinctId: string, item: string) => { + if (!MIXPANEL_TOKEN) return; + mixpanelClient().track(`user creates ${item}`, { distinct_id: distinctId }); + }, +}; +export default { mixpanelClient, track }; diff --git a/apps/auth/src/app/util/oidc.ts b/apps/auth/src/app/util/oidc.ts new file mode 100644 index 000000000..21804516a --- /dev/null +++ b/apps/auth/src/app/util/oidc.ts @@ -0,0 +1,17 @@ +import { Provider } from 'oidc-provider'; +import configuration from '../config/oidc'; +import { AUTH_URL, NODE_ENV } from '../config/secrets'; + +const oidc = new Provider(AUTH_URL, configuration); + +oidc.proxy = true; + +if (NODE_ENV !== 'production') { + const { invalidate: orig } = (oidc.Client as any).Schema.prototype; + (oidc.Client as any).Schema.prototype.invalidate = function invalidate(message, code) { + if (code === 'implicit-force-https' || code === 'implicit-forbid-localhost') return; + orig.call(this, message); + }; +} + +export { oidc }; diff --git a/apps/auth/src/app/util/passwordcheck.ts b/apps/auth/src/app/util/passwordcheck.ts new file mode 100644 index 000000000..3d91748a9 --- /dev/null +++ b/apps/auth/src/app/util/passwordcheck.ts @@ -0,0 +1,13 @@ +export const checkPasswordStrength = (password: string) => { + const strongPassword = new RegExp('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{8,})'); + const mediumPassword = new RegExp( + '((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{6,}))|((?=.*[a-z])(?=.*[A-Z])(?=.*[^A-Za-z0-9])(?=.{8,}))', + ); + if (strongPassword.test(password)) { + return 'strong'; + } else if (mediumPassword.test(password)) { + return 'medium'; + } else { + return 'weak'; + } +}; diff --git a/apps/auth/src/app/util/path.ts b/apps/auth/src/app/util/path.ts new file mode 100644 index 000000000..98377c9a6 --- /dev/null +++ b/apps/auth/src/app/util/path.ts @@ -0,0 +1,4 @@ +import path from 'path'; +import { CWD } from '../config/secrets'; + +export const assetsPath = path.resolve(CWD, 'assets'); diff --git a/apps/auth/src/app/util/ratelimiter.ts b/apps/auth/src/app/util/ratelimiter.ts new file mode 100644 index 000000000..7ed5a76de --- /dev/null +++ b/apps/auth/src/app/util/ratelimiter.ts @@ -0,0 +1,6 @@ +import rateLimit from 'express-rate-limit'; + +export const rateLimitRewardGive = rateLimit({ + windowMs: 3600 * 1000, // in seconds * 1000ms + max: 10, // limit each IP to n requests per windowMs +}); diff --git a/apps/auth/src/app/util/time.ts b/apps/auth/src/app/util/time.ts new file mode 100644 index 000000000..24fcae879 --- /dev/null +++ b/apps/auth/src/app/util/time.ts @@ -0,0 +1 @@ +export const get24HoursExpiryTimestamp = () => Date.now() + 1000 * 60 * 60 * 24; diff --git a/apps/auth/src/app/util/tokens.ts b/apps/auth/src/app/util/tokens.ts new file mode 100644 index 000000000..786573023 --- /dev/null +++ b/apps/auth/src/app/util/tokens.ts @@ -0,0 +1,6 @@ +import crypto from 'crypto'; + +export function createRandomToken() { + const buf = crypto.randomBytes(16); + return buf.toString('hex'); +} diff --git a/apps/auth/src/app/util/validate.ts b/apps/auth/src/app/util/validate.ts new file mode 100644 index 000000000..488cdb5a4 --- /dev/null +++ b/apps/auth/src/app/util/validate.ts @@ -0,0 +1,24 @@ +import { ValidationChain, validationResult } from 'express-validator'; +import { Response, Request, NextFunction } from 'express'; + +export const validate = (validations: ValidationChain[]) => { + return async (req: Request, res: Response, next: NextFunction) => { + await Promise.all(validations.map((validation) => validation.run(req))); + + const errors = validationResult(req); + + if (errors.isEmpty()) { + return next(); + } + + res.status(400).json({ errors: errors.array() }); + }; +}; + +export function validateEmail(email: string) { + return String(email) + .toLowerCase() + .match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + ); +} diff --git a/apps/auth/src/assets/browserconfig.xml b/apps/auth/src/assets/browserconfig.xml new file mode 100644 index 000000000..b2f4bd3b8 --- /dev/null +++ b/apps/auth/src/assets/browserconfig.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<browserconfig> + <msapplication> + <tile> + <square150x150logo src="/mstile-150x150.png?v=pgdmXmoa2w"/> + <TileColor>#da532c</TileColor> + </tile> + </msapplication> +</browserconfig> diff --git a/apps/auth/src/assets/css/bootstrap.min.css b/apps/auth/src/assets/css/bootstrap.min.css new file mode 100644 index 000000000..86b6845bc --- /dev/null +++ b/apps/auth/src/assets/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal .list-group-item.active{margin-top:0}.list-group-horizontal .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm .list-group-item.active{margin-top:0}.list-group-horizontal-sm .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md .list-group-item.active{margin-top:0}.list-group-horizontal-md .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg .list-group-item.active{margin-top:0}.list-group-horizontal-lg .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl .list-group-item.active{margin-top:0}.list-group-horizontal-xl .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/apps/auth/src/assets/css/main.css b/apps/auth/src/assets/css/main.css new file mode 100644 index 000000000..4ee3da5a6 --- /dev/null +++ b/apps/auth/src/assets/css/main.css @@ -0,0 +1,9109 @@ +@charset "UTF-8"; +@import url("https://fonts.googleapis.com/css?family=Exo+2:200,400,400i,700,700i,900,900i"); +@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap"); +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block; +} + +body { + margin: 0; + font-family: "Exo 2", BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; +} + +[tabindex="-1"]:focus:not(:focus-visible) { + outline: 0 !important; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + text-decoration-skip-ink: none; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 700; +} + +dd { + margin-bottom: 0.5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +b, +strong { + font-weight: bolder; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +a { + color: #5942c1; + text-decoration: none; + background-color: transparent; +} +a:hover { + color: #3e2d89; + text-decoration: underline; +} + +a:not([href]):not([class]) { + color: inherit; + text-decoration: none; +} +a:not([href]):not([class]):hover { + color: inherit; + text-decoration: none; +} + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + -ms-overflow-style: scrollbar; +} + +figure { + margin: 0 0 1rem; +} + +img { + vertical-align: middle; + border-style: none; +} + +svg { + overflow: hidden; + vertical-align: middle; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #6c757d; + text-align: left; + caption-side: bottom; +} + +th { + text-align: inherit; + text-align: -webkit-match-parent; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +button { + border-radius: 0; +} + +button:focus:not(:focus-visible) { + outline: 0; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +[role=button] { + cursor: pointer; +} + +select { + word-wrap: normal; +} + +button, +[type=button], +[type=reset], +[type=submit] { + -webkit-appearance: button; +} + +button:not(:disabled), +[type=button]:not(:disabled), +[type=reset]:not(:disabled), +[type=submit]:not(:disabled) { + cursor: pointer; +} + +button::-moz-focus-inner, +[type=button]::-moz-focus-inner, +[type=reset]::-moz-focus-inner, +[type=submit]::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type=radio], +input[type=checkbox] { + box-sizing: border-box; + padding: 0; +} + +textarea { + overflow: auto; + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; +} +@media (max-width: 1200px) { + legend { + font-size: calc(1.275rem + 0.3vw); + } +} + +progress { + vertical-align: baseline; +} + +[type=number]::-webkit-inner-spin-button, +[type=number]::-webkit-outer-spin-button { + height: auto; +} + +[type=search] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +summary { + display: list-item; + cursor: pointer; +} + +template { + display: none; +} + +[hidden] { + display: none !important; +} + +.align-baseline { + vertical-align: baseline !important; +} + +.align-top { + vertical-align: top !important; +} + +.align-middle { + vertical-align: middle !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.align-text-bottom { + vertical-align: text-bottom !important; +} + +.align-text-top { + vertical-align: text-top !important; +} + +.bg-primary { + background-color: #5942c1 !important; +} + +a.bg-primary:hover, a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #46339d !important; +} + +.bg-secondary { + background-color: #ffe500 !important; +} + +a.bg-secondary:hover, a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #ccb700 !important; +} + +.bg-success { + background-color: #28a745 !important; +} + +a.bg-success:hover, a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #1e7e34 !important; +} + +.bg-info { + background-color: #17a2b8 !important; +} + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #117a8b !important; +} + +.bg-warning { + background-color: #ffe500 !important; +} + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #ccb700 !important; +} + +.bg-danger { + background-color: #dc3545 !important; +} + +a.bg-danger:hover, a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #bd2130 !important; +} + +.bg-light { + background-color: #f8f9fa !important; +} + +a.bg-light:hover, a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #dae0e5 !important; +} + +.bg-dark { + background-color: #343a40 !important; +} + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #1d2124 !important; +} + +.bg-gray { + background-color: #9c9e9f !important; +} + +a.bg-gray:hover, a.bg-gray:focus, +button.bg-gray:hover, +button.bg-gray:focus { + background-color: #828586 !important; +} + +.bg-darker { + background-color: #212529 !important; +} + +a.bg-darker:hover, a.bg-darker:focus, +button.bg-darker:hover, +button.bg-darker:focus { + background-color: #0a0c0d !important; +} + +.bg-white { + background-color: #fff !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border { + border: 1px solid #dee2e6 !important; +} + +.border-top { + border-top: 1px solid #dee2e6 !important; +} + +.border-right { + border-right: 1px solid #dee2e6 !important; +} + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important; +} + +.border-left { + border-left: 1px solid #dee2e6 !important; +} + +.border-0 { + border: 0 !important; +} + +.border-top-0 { + border-top: 0 !important; +} + +.border-right-0 { + border-right: 0 !important; +} + +.border-bottom-0 { + border-bottom: 0 !important; +} + +.border-left-0 { + border-left: 0 !important; +} + +.border-primary { + border-color: #5942c1 !important; +} + +.border-secondary { + border-color: #ffe500 !important; +} + +.border-success { + border-color: #28a745 !important; +} + +.border-info { + border-color: #17a2b8 !important; +} + +.border-warning { + border-color: #ffe500 !important; +} + +.border-danger { + border-color: #dc3545 !important; +} + +.border-light { + border-color: #f8f9fa !important; +} + +.border-dark { + border-color: #343a40 !important; +} + +.border-gray { + border-color: #9c9e9f !important; +} + +.border-darker { + border-color: #212529 !important; +} + +.border-white { + border-color: #fff !important; +} + +.rounded-sm { + border-radius: 0.2rem !important; +} + +.rounded { + border-radius: 0.3rem !important; +} + +.rounded-top { + border-top-left-radius: 0.3rem !important; + border-top-right-radius: 0.3rem !important; +} + +.rounded-right { + border-top-right-radius: 0.3rem !important; + border-bottom-right-radius: 0.3rem !important; +} + +.rounded-bottom { + border-bottom-right-radius: 0.3rem !important; + border-bottom-left-radius: 0.3rem !important; +} + +.rounded-left { + border-top-left-radius: 0.3rem !important; + border-bottom-left-radius: 0.3rem !important; +} + +.rounded-lg { + border-radius: 0.3rem !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.rounded-pill { + border-radius: 50rem !important; +} + +.rounded-0 { + border-radius: 0 !important; +} + +.clearfix::after { + display: block; + clear: both; + content: ""; +} + +.d-none { + display: none !important; +} + +.d-inline { + display: inline !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.d-block { + display: block !important; +} + +.d-table { + display: table !important; +} + +.d-table-row { + display: table-row !important; +} + +.d-table-cell { + display: table-cell !important; +} + +.d-flex { + display: flex !important; +} + +.d-inline-flex { + display: inline-flex !important; +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; + } + + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } +} +@media (min-width: 768px) { + .d-md-none { + display: none !important; + } + + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } +} +@media (min-width: 992px) { + .d-lg-none { + display: none !important; + } + + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } +} +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; + } + + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } +} +@media print { + .d-print-none { + display: none !important; + } + + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } +} +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; +} +.embed-responsive::before { + display: block; + content: ""; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-21by9::before { + padding-top: 42.8571428571%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-4by3::before { + padding-top: 75%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.embed-responsive-21by9::before { + padding-top: 42.8571428571%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-4by3::before { + padding-top: 75%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.flex-row { + flex-direction: row !important; +} + +.flex-column { + flex-direction: column !important; +} + +.flex-row-reverse { + flex-direction: row-reverse !important; +} + +.flex-column-reverse { + flex-direction: column-reverse !important; +} + +.flex-wrap { + flex-wrap: wrap !important; +} + +.flex-nowrap { + flex-wrap: nowrap !important; +} + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; +} + +.flex-fill { + flex: 1 1 auto !important; +} + +.flex-grow-0 { + flex-grow: 0 !important; +} + +.flex-grow-1 { + flex-grow: 1 !important; +} + +.flex-shrink-0 { + flex-shrink: 0 !important; +} + +.flex-shrink-1 { + flex-shrink: 1 !important; +} + +.justify-content-start { + justify-content: flex-start !important; +} + +.justify-content-end { + justify-content: flex-end !important; +} + +.justify-content-center { + justify-content: center !important; +} + +.justify-content-between { + justify-content: space-between !important; +} + +.justify-content-around { + justify-content: space-around !important; +} + +.align-items-start { + align-items: flex-start !important; +} + +.align-items-end { + align-items: flex-end !important; +} + +.align-items-center { + align-items: center !important; +} + +.align-items-baseline { + align-items: baseline !important; +} + +.align-items-stretch { + align-items: stretch !important; +} + +.align-content-start { + align-content: flex-start !important; +} + +.align-content-end { + align-content: flex-end !important; +} + +.align-content-center { + align-content: center !important; +} + +.align-content-between { + align-content: space-between !important; +} + +.align-content-around { + align-content: space-around !important; +} + +.align-content-stretch { + align-content: stretch !important; +} + +.align-self-auto { + align-self: auto !important; +} + +.align-self-start { + align-self: flex-start !important; +} + +.align-self-end { + align-self: flex-end !important; +} + +.align-self-center { + align-self: center !important; +} + +.align-self-baseline { + align-self: baseline !important; +} + +.align-self-stretch { + align-self: stretch !important; +} + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } +} +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } +} +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } +} +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } +} +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.float-none { + float: none !important; +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; + } + + .float-sm-right { + float: right !important; + } + + .float-sm-none { + float: none !important; + } +} +@media (min-width: 768px) { + .float-md-left { + float: left !important; + } + + .float-md-right { + float: right !important; + } + + .float-md-none { + float: none !important; + } +} +@media (min-width: 992px) { + .float-lg-left { + float: left !important; + } + + .float-lg-right { + float: right !important; + } + + .float-lg-none { + float: none !important; + } +} +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; + } + + .float-xl-right { + float: right !important; + } + + .float-xl-none { + float: none !important; + } +} +.user-select-all { + user-select: all !important; +} + +.user-select-auto { + user-select: auto !important; +} + +.user-select-none { + user-select: none !important; +} + +.overflow-auto { + overflow: auto !important; +} + +.overflow-hidden { + overflow: hidden !important; +} + +.position-static { + position: static !important; +} + +.position-relative { + position: relative !important; +} + +.position-absolute { + position: absolute !important; +} + +.position-fixed { + position: fixed !important; +} + +.position-sticky { + position: sticky !important; +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; +} + +@supports (position: sticky) { + .sticky-top { + position: sticky; + top: 0; + z-index: 1020; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; +} + +.shadow-sm { + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; +} + +.shadow { + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; +} + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; +} + +.shadow-none { + box-shadow: none !important; +} + +.w-25 { + width: 25% !important; +} + +.w-50 { + width: 50% !important; +} + +.w-75 { + width: 75% !important; +} + +.w-100 { + width: 100% !important; +} + +.w-auto { + width: auto !important; +} + +.h-25 { + height: 25% !important; +} + +.h-50 { + height: 50% !important; +} + +.h-75 { + height: 75% !important; +} + +.h-100 { + height: 100% !important; +} + +.h-auto { + height: auto !important; +} + +.mw-100 { + max-width: 100% !important; +} + +.mh-100 { + max-height: 100% !important; +} + +.min-vw-100 { + min-width: 100vw !important; +} + +.min-vh-100 { + min-height: 100vh !important; +} + +.vw-100 { + width: 100vw !important; +} + +.vh-100 { + height: 100vh !important; +} + +.m-0 { + margin: 0 !important; +} + +.mt-0, +.my-0 { + margin-top: 0 !important; +} + +.mr-0, +.mx-0 { + margin-right: 0 !important; +} + +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} + +.ml-0, +.mx-0 { + margin-left: 0 !important; +} + +.m-1 { + margin: 0.25rem !important; +} + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; +} + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; +} + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} + +.m-2 { + margin: 0.5rem !important; +} + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} + +.m-3 { + margin: 1rem !important; +} + +.mt-3, +.my-3 { + margin-top: 1rem !important; +} + +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; +} + +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} + +.m-4 { + margin: 1.5rem !important; +} + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; +} + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} + +.m-5 { + margin: 3rem !important; +} + +.mt-5, +.my-5 { + margin-top: 3rem !important; +} + +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} + +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} + +.p-0 { + padding: 0 !important; +} + +.pt-0, +.py-0 { + padding-top: 0 !important; +} + +.pr-0, +.px-0 { + padding-right: 0 !important; +} + +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} + +.pl-0, +.px-0 { + padding-left: 0 !important; +} + +.p-1 { + padding: 0.25rem !important; +} + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} + +.p-2 { + padding: 0.5rem !important; +} + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} + +.p-3 { + padding: 1rem !important; +} + +.pt-3, +.py-3 { + padding-top: 1rem !important; +} + +.pr-3, +.px-3 { + padding-right: 1rem !important; +} + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} + +.pl-3, +.px-3 { + padding-left: 1rem !important; +} + +.p-4 { + padding: 1.5rem !important; +} + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; +} + +.pt-5, +.py-5 { + padding-top: 3rem !important; +} + +.pr-5, +.px-5 { + padding-right: 3rem !important; +} + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} + +.pl-5, +.px-5 { + padding-left: 3rem !important; +} + +.m-n1 { + margin: -0.25rem !important; +} + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} + +.m-n2 { + margin: -0.5rem !important; +} + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} + +.m-n3 { + margin: -1rem !important; +} + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} + +.m-n4 { + margin: -1.5rem !important; +} + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} + +.m-n5 { + margin: -3rem !important; +} + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} + +.m-auto { + margin: auto !important; +} + +.mt-auto, +.my-auto { + margin-top: auto !important; +} + +.mr-auto, +.mx-auto { + margin-right: auto !important; +} + +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} + +.ml-auto, +.mx-auto { + margin-left: auto !important; +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; + } + + .mt-sm-0, +.my-sm-0 { + margin-top: 0 !important; + } + + .mr-sm-0, +.mx-sm-0 { + margin-right: 0 !important; + } + + .mb-sm-0, +.my-sm-0 { + margin-bottom: 0 !important; + } + + .ml-sm-0, +.mx-sm-0 { + margin-left: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .mt-sm-1, +.my-sm-1 { + margin-top: 0.25rem !important; + } + + .mr-sm-1, +.mx-sm-1 { + margin-right: 0.25rem !important; + } + + .mb-sm-1, +.my-sm-1 { + margin-bottom: 0.25rem !important; + } + + .ml-sm-1, +.mx-sm-1 { + margin-left: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .mt-sm-2, +.my-sm-2 { + margin-top: 0.5rem !important; + } + + .mr-sm-2, +.mx-sm-2 { + margin-right: 0.5rem !important; + } + + .mb-sm-2, +.my-sm-2 { + margin-bottom: 0.5rem !important; + } + + .ml-sm-2, +.mx-sm-2 { + margin-left: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .mt-sm-3, +.my-sm-3 { + margin-top: 1rem !important; + } + + .mr-sm-3, +.mx-sm-3 { + margin-right: 1rem !important; + } + + .mb-sm-3, +.my-sm-3 { + margin-bottom: 1rem !important; + } + + .ml-sm-3, +.mx-sm-3 { + margin-left: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .mt-sm-4, +.my-sm-4 { + margin-top: 1.5rem !important; + } + + .mr-sm-4, +.mx-sm-4 { + margin-right: 1.5rem !important; + } + + .mb-sm-4, +.my-sm-4 { + margin-bottom: 1.5rem !important; + } + + .ml-sm-4, +.mx-sm-4 { + margin-left: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .mt-sm-5, +.my-sm-5 { + margin-top: 3rem !important; + } + + .mr-sm-5, +.mx-sm-5 { + margin-right: 3rem !important; + } + + .mb-sm-5, +.my-sm-5 { + margin-bottom: 3rem !important; + } + + .ml-sm-5, +.mx-sm-5 { + margin-left: 3rem !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .pt-sm-0, +.py-sm-0 { + padding-top: 0 !important; + } + + .pr-sm-0, +.px-sm-0 { + padding-right: 0 !important; + } + + .pb-sm-0, +.py-sm-0 { + padding-bottom: 0 !important; + } + + .pl-sm-0, +.px-sm-0 { + padding-left: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .pt-sm-1, +.py-sm-1 { + padding-top: 0.25rem !important; + } + + .pr-sm-1, +.px-sm-1 { + padding-right: 0.25rem !important; + } + + .pb-sm-1, +.py-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pl-sm-1, +.px-sm-1 { + padding-left: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .pt-sm-2, +.py-sm-2 { + padding-top: 0.5rem !important; + } + + .pr-sm-2, +.px-sm-2 { + padding-right: 0.5rem !important; + } + + .pb-sm-2, +.py-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pl-sm-2, +.px-sm-2 { + padding-left: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .pt-sm-3, +.py-sm-3 { + padding-top: 1rem !important; + } + + .pr-sm-3, +.px-sm-3 { + padding-right: 1rem !important; + } + + .pb-sm-3, +.py-sm-3 { + padding-bottom: 1rem !important; + } + + .pl-sm-3, +.px-sm-3 { + padding-left: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .pt-sm-4, +.py-sm-4 { + padding-top: 1.5rem !important; + } + + .pr-sm-4, +.px-sm-4 { + padding-right: 1.5rem !important; + } + + .pb-sm-4, +.py-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pl-sm-4, +.px-sm-4 { + padding-left: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .pt-sm-5, +.py-sm-5 { + padding-top: 3rem !important; + } + + .pr-sm-5, +.px-sm-5 { + padding-right: 3rem !important; + } + + .pb-sm-5, +.py-sm-5 { + padding-bottom: 3rem !important; + } + + .pl-sm-5, +.px-sm-5 { + padding-left: 3rem !important; + } + + .m-sm-n1 { + margin: -0.25rem !important; + } + + .mt-sm-n1, +.my-sm-n1 { + margin-top: -0.25rem !important; + } + + .mr-sm-n1, +.mx-sm-n1 { + margin-right: -0.25rem !important; + } + + .mb-sm-n1, +.my-sm-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-sm-n1, +.mx-sm-n1 { + margin-left: -0.25rem !important; + } + + .m-sm-n2 { + margin: -0.5rem !important; + } + + .mt-sm-n2, +.my-sm-n2 { + margin-top: -0.5rem !important; + } + + .mr-sm-n2, +.mx-sm-n2 { + margin-right: -0.5rem !important; + } + + .mb-sm-n2, +.my-sm-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-sm-n2, +.mx-sm-n2 { + margin-left: -0.5rem !important; + } + + .m-sm-n3 { + margin: -1rem !important; + } + + .mt-sm-n3, +.my-sm-n3 { + margin-top: -1rem !important; + } + + .mr-sm-n3, +.mx-sm-n3 { + margin-right: -1rem !important; + } + + .mb-sm-n3, +.my-sm-n3 { + margin-bottom: -1rem !important; + } + + .ml-sm-n3, +.mx-sm-n3 { + margin-left: -1rem !important; + } + + .m-sm-n4 { + margin: -1.5rem !important; + } + + .mt-sm-n4, +.my-sm-n4 { + margin-top: -1.5rem !important; + } + + .mr-sm-n4, +.mx-sm-n4 { + margin-right: -1.5rem !important; + } + + .mb-sm-n4, +.my-sm-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-sm-n4, +.mx-sm-n4 { + margin-left: -1.5rem !important; + } + + .m-sm-n5 { + margin: -3rem !important; + } + + .mt-sm-n5, +.my-sm-n5 { + margin-top: -3rem !important; + } + + .mr-sm-n5, +.mx-sm-n5 { + margin-right: -3rem !important; + } + + .mb-sm-n5, +.my-sm-n5 { + margin-bottom: -3rem !important; + } + + .ml-sm-n5, +.mx-sm-n5 { + margin-left: -3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mt-sm-auto, +.my-sm-auto { + margin-top: auto !important; + } + + .mr-sm-auto, +.mx-sm-auto { + margin-right: auto !important; + } + + .mb-sm-auto, +.my-sm-auto { + margin-bottom: auto !important; + } + + .ml-sm-auto, +.mx-sm-auto { + margin-left: auto !important; + } +} +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; + } + + .mt-md-0, +.my-md-0 { + margin-top: 0 !important; + } + + .mr-md-0, +.mx-md-0 { + margin-right: 0 !important; + } + + .mb-md-0, +.my-md-0 { + margin-bottom: 0 !important; + } + + .ml-md-0, +.mx-md-0 { + margin-left: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .mt-md-1, +.my-md-1 { + margin-top: 0.25rem !important; + } + + .mr-md-1, +.mx-md-1 { + margin-right: 0.25rem !important; + } + + .mb-md-1, +.my-md-1 { + margin-bottom: 0.25rem !important; + } + + .ml-md-1, +.mx-md-1 { + margin-left: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .mt-md-2, +.my-md-2 { + margin-top: 0.5rem !important; + } + + .mr-md-2, +.mx-md-2 { + margin-right: 0.5rem !important; + } + + .mb-md-2, +.my-md-2 { + margin-bottom: 0.5rem !important; + } + + .ml-md-2, +.mx-md-2 { + margin-left: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .mt-md-3, +.my-md-3 { + margin-top: 1rem !important; + } + + .mr-md-3, +.mx-md-3 { + margin-right: 1rem !important; + } + + .mb-md-3, +.my-md-3 { + margin-bottom: 1rem !important; + } + + .ml-md-3, +.mx-md-3 { + margin-left: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .mt-md-4, +.my-md-4 { + margin-top: 1.5rem !important; + } + + .mr-md-4, +.mx-md-4 { + margin-right: 1.5rem !important; + } + + .mb-md-4, +.my-md-4 { + margin-bottom: 1.5rem !important; + } + + .ml-md-4, +.mx-md-4 { + margin-left: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .mt-md-5, +.my-md-5 { + margin-top: 3rem !important; + } + + .mr-md-5, +.mx-md-5 { + margin-right: 3rem !important; + } + + .mb-md-5, +.my-md-5 { + margin-bottom: 3rem !important; + } + + .ml-md-5, +.mx-md-5 { + margin-left: 3rem !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .pt-md-0, +.py-md-0 { + padding-top: 0 !important; + } + + .pr-md-0, +.px-md-0 { + padding-right: 0 !important; + } + + .pb-md-0, +.py-md-0 { + padding-bottom: 0 !important; + } + + .pl-md-0, +.px-md-0 { + padding-left: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .pt-md-1, +.py-md-1 { + padding-top: 0.25rem !important; + } + + .pr-md-1, +.px-md-1 { + padding-right: 0.25rem !important; + } + + .pb-md-1, +.py-md-1 { + padding-bottom: 0.25rem !important; + } + + .pl-md-1, +.px-md-1 { + padding-left: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .pt-md-2, +.py-md-2 { + padding-top: 0.5rem !important; + } + + .pr-md-2, +.px-md-2 { + padding-right: 0.5rem !important; + } + + .pb-md-2, +.py-md-2 { + padding-bottom: 0.5rem !important; + } + + .pl-md-2, +.px-md-2 { + padding-left: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .pt-md-3, +.py-md-3 { + padding-top: 1rem !important; + } + + .pr-md-3, +.px-md-3 { + padding-right: 1rem !important; + } + + .pb-md-3, +.py-md-3 { + padding-bottom: 1rem !important; + } + + .pl-md-3, +.px-md-3 { + padding-left: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .pt-md-4, +.py-md-4 { + padding-top: 1.5rem !important; + } + + .pr-md-4, +.px-md-4 { + padding-right: 1.5rem !important; + } + + .pb-md-4, +.py-md-4 { + padding-bottom: 1.5rem !important; + } + + .pl-md-4, +.px-md-4 { + padding-left: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .pt-md-5, +.py-md-5 { + padding-top: 3rem !important; + } + + .pr-md-5, +.px-md-5 { + padding-right: 3rem !important; + } + + .pb-md-5, +.py-md-5 { + padding-bottom: 3rem !important; + } + + .pl-md-5, +.px-md-5 { + padding-left: 3rem !important; + } + + .m-md-n1 { + margin: -0.25rem !important; + } + + .mt-md-n1, +.my-md-n1 { + margin-top: -0.25rem !important; + } + + .mr-md-n1, +.mx-md-n1 { + margin-right: -0.25rem !important; + } + + .mb-md-n1, +.my-md-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-md-n1, +.mx-md-n1 { + margin-left: -0.25rem !important; + } + + .m-md-n2 { + margin: -0.5rem !important; + } + + .mt-md-n2, +.my-md-n2 { + margin-top: -0.5rem !important; + } + + .mr-md-n2, +.mx-md-n2 { + margin-right: -0.5rem !important; + } + + .mb-md-n2, +.my-md-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-md-n2, +.mx-md-n2 { + margin-left: -0.5rem !important; + } + + .m-md-n3 { + margin: -1rem !important; + } + + .mt-md-n3, +.my-md-n3 { + margin-top: -1rem !important; + } + + .mr-md-n3, +.mx-md-n3 { + margin-right: -1rem !important; + } + + .mb-md-n3, +.my-md-n3 { + margin-bottom: -1rem !important; + } + + .ml-md-n3, +.mx-md-n3 { + margin-left: -1rem !important; + } + + .m-md-n4 { + margin: -1.5rem !important; + } + + .mt-md-n4, +.my-md-n4 { + margin-top: -1.5rem !important; + } + + .mr-md-n4, +.mx-md-n4 { + margin-right: -1.5rem !important; + } + + .mb-md-n4, +.my-md-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-md-n4, +.mx-md-n4 { + margin-left: -1.5rem !important; + } + + .m-md-n5 { + margin: -3rem !important; + } + + .mt-md-n5, +.my-md-n5 { + margin-top: -3rem !important; + } + + .mr-md-n5, +.mx-md-n5 { + margin-right: -3rem !important; + } + + .mb-md-n5, +.my-md-n5 { + margin-bottom: -3rem !important; + } + + .ml-md-n5, +.mx-md-n5 { + margin-left: -3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mt-md-auto, +.my-md-auto { + margin-top: auto !important; + } + + .mr-md-auto, +.mx-md-auto { + margin-right: auto !important; + } + + .mb-md-auto, +.my-md-auto { + margin-bottom: auto !important; + } + + .ml-md-auto, +.mx-md-auto { + margin-left: auto !important; + } +} +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; + } + + .mt-lg-0, +.my-lg-0 { + margin-top: 0 !important; + } + + .mr-lg-0, +.mx-lg-0 { + margin-right: 0 !important; + } + + .mb-lg-0, +.my-lg-0 { + margin-bottom: 0 !important; + } + + .ml-lg-0, +.mx-lg-0 { + margin-left: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .mt-lg-1, +.my-lg-1 { + margin-top: 0.25rem !important; + } + + .mr-lg-1, +.mx-lg-1 { + margin-right: 0.25rem !important; + } + + .mb-lg-1, +.my-lg-1 { + margin-bottom: 0.25rem !important; + } + + .ml-lg-1, +.mx-lg-1 { + margin-left: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .mt-lg-2, +.my-lg-2 { + margin-top: 0.5rem !important; + } + + .mr-lg-2, +.mx-lg-2 { + margin-right: 0.5rem !important; + } + + .mb-lg-2, +.my-lg-2 { + margin-bottom: 0.5rem !important; + } + + .ml-lg-2, +.mx-lg-2 { + margin-left: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .mt-lg-3, +.my-lg-3 { + margin-top: 1rem !important; + } + + .mr-lg-3, +.mx-lg-3 { + margin-right: 1rem !important; + } + + .mb-lg-3, +.my-lg-3 { + margin-bottom: 1rem !important; + } + + .ml-lg-3, +.mx-lg-3 { + margin-left: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .mt-lg-4, +.my-lg-4 { + margin-top: 1.5rem !important; + } + + .mr-lg-4, +.mx-lg-4 { + margin-right: 1.5rem !important; + } + + .mb-lg-4, +.my-lg-4 { + margin-bottom: 1.5rem !important; + } + + .ml-lg-4, +.mx-lg-4 { + margin-left: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .mt-lg-5, +.my-lg-5 { + margin-top: 3rem !important; + } + + .mr-lg-5, +.mx-lg-5 { + margin-right: 3rem !important; + } + + .mb-lg-5, +.my-lg-5 { + margin-bottom: 3rem !important; + } + + .ml-lg-5, +.mx-lg-5 { + margin-left: 3rem !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .pt-lg-0, +.py-lg-0 { + padding-top: 0 !important; + } + + .pr-lg-0, +.px-lg-0 { + padding-right: 0 !important; + } + + .pb-lg-0, +.py-lg-0 { + padding-bottom: 0 !important; + } + + .pl-lg-0, +.px-lg-0 { + padding-left: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .pt-lg-1, +.py-lg-1 { + padding-top: 0.25rem !important; + } + + .pr-lg-1, +.px-lg-1 { + padding-right: 0.25rem !important; + } + + .pb-lg-1, +.py-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pl-lg-1, +.px-lg-1 { + padding-left: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .pt-lg-2, +.py-lg-2 { + padding-top: 0.5rem !important; + } + + .pr-lg-2, +.px-lg-2 { + padding-right: 0.5rem !important; + } + + .pb-lg-2, +.py-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pl-lg-2, +.px-lg-2 { + padding-left: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .pt-lg-3, +.py-lg-3 { + padding-top: 1rem !important; + } + + .pr-lg-3, +.px-lg-3 { + padding-right: 1rem !important; + } + + .pb-lg-3, +.py-lg-3 { + padding-bottom: 1rem !important; + } + + .pl-lg-3, +.px-lg-3 { + padding-left: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .pt-lg-4, +.py-lg-4 { + padding-top: 1.5rem !important; + } + + .pr-lg-4, +.px-lg-4 { + padding-right: 1.5rem !important; + } + + .pb-lg-4, +.py-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pl-lg-4, +.px-lg-4 { + padding-left: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .pt-lg-5, +.py-lg-5 { + padding-top: 3rem !important; + } + + .pr-lg-5, +.px-lg-5 { + padding-right: 3rem !important; + } + + .pb-lg-5, +.py-lg-5 { + padding-bottom: 3rem !important; + } + + .pl-lg-5, +.px-lg-5 { + padding-left: 3rem !important; + } + + .m-lg-n1 { + margin: -0.25rem !important; + } + + .mt-lg-n1, +.my-lg-n1 { + margin-top: -0.25rem !important; + } + + .mr-lg-n1, +.mx-lg-n1 { + margin-right: -0.25rem !important; + } + + .mb-lg-n1, +.my-lg-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-lg-n1, +.mx-lg-n1 { + margin-left: -0.25rem !important; + } + + .m-lg-n2 { + margin: -0.5rem !important; + } + + .mt-lg-n2, +.my-lg-n2 { + margin-top: -0.5rem !important; + } + + .mr-lg-n2, +.mx-lg-n2 { + margin-right: -0.5rem !important; + } + + .mb-lg-n2, +.my-lg-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-lg-n2, +.mx-lg-n2 { + margin-left: -0.5rem !important; + } + + .m-lg-n3 { + margin: -1rem !important; + } + + .mt-lg-n3, +.my-lg-n3 { + margin-top: -1rem !important; + } + + .mr-lg-n3, +.mx-lg-n3 { + margin-right: -1rem !important; + } + + .mb-lg-n3, +.my-lg-n3 { + margin-bottom: -1rem !important; + } + + .ml-lg-n3, +.mx-lg-n3 { + margin-left: -1rem !important; + } + + .m-lg-n4 { + margin: -1.5rem !important; + } + + .mt-lg-n4, +.my-lg-n4 { + margin-top: -1.5rem !important; + } + + .mr-lg-n4, +.mx-lg-n4 { + margin-right: -1.5rem !important; + } + + .mb-lg-n4, +.my-lg-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-lg-n4, +.mx-lg-n4 { + margin-left: -1.5rem !important; + } + + .m-lg-n5 { + margin: -3rem !important; + } + + .mt-lg-n5, +.my-lg-n5 { + margin-top: -3rem !important; + } + + .mr-lg-n5, +.mx-lg-n5 { + margin-right: -3rem !important; + } + + .mb-lg-n5, +.my-lg-n5 { + margin-bottom: -3rem !important; + } + + .ml-lg-n5, +.mx-lg-n5 { + margin-left: -3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mt-lg-auto, +.my-lg-auto { + margin-top: auto !important; + } + + .mr-lg-auto, +.mx-lg-auto { + margin-right: auto !important; + } + + .mb-lg-auto, +.my-lg-auto { + margin-bottom: auto !important; + } + + .ml-lg-auto, +.mx-lg-auto { + margin-left: auto !important; + } +} +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; + } + + .mt-xl-0, +.my-xl-0 { + margin-top: 0 !important; + } + + .mr-xl-0, +.mx-xl-0 { + margin-right: 0 !important; + } + + .mb-xl-0, +.my-xl-0 { + margin-bottom: 0 !important; + } + + .ml-xl-0, +.mx-xl-0 { + margin-left: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .mt-xl-1, +.my-xl-1 { + margin-top: 0.25rem !important; + } + + .mr-xl-1, +.mx-xl-1 { + margin-right: 0.25rem !important; + } + + .mb-xl-1, +.my-xl-1 { + margin-bottom: 0.25rem !important; + } + + .ml-xl-1, +.mx-xl-1 { + margin-left: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .mt-xl-2, +.my-xl-2 { + margin-top: 0.5rem !important; + } + + .mr-xl-2, +.mx-xl-2 { + margin-right: 0.5rem !important; + } + + .mb-xl-2, +.my-xl-2 { + margin-bottom: 0.5rem !important; + } + + .ml-xl-2, +.mx-xl-2 { + margin-left: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .mt-xl-3, +.my-xl-3 { + margin-top: 1rem !important; + } + + .mr-xl-3, +.mx-xl-3 { + margin-right: 1rem !important; + } + + .mb-xl-3, +.my-xl-3 { + margin-bottom: 1rem !important; + } + + .ml-xl-3, +.mx-xl-3 { + margin-left: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .mt-xl-4, +.my-xl-4 { + margin-top: 1.5rem !important; + } + + .mr-xl-4, +.mx-xl-4 { + margin-right: 1.5rem !important; + } + + .mb-xl-4, +.my-xl-4 { + margin-bottom: 1.5rem !important; + } + + .ml-xl-4, +.mx-xl-4 { + margin-left: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .mt-xl-5, +.my-xl-5 { + margin-top: 3rem !important; + } + + .mr-xl-5, +.mx-xl-5 { + margin-right: 3rem !important; + } + + .mb-xl-5, +.my-xl-5 { + margin-bottom: 3rem !important; + } + + .ml-xl-5, +.mx-xl-5 { + margin-left: 3rem !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .pt-xl-0, +.py-xl-0 { + padding-top: 0 !important; + } + + .pr-xl-0, +.px-xl-0 { + padding-right: 0 !important; + } + + .pb-xl-0, +.py-xl-0 { + padding-bottom: 0 !important; + } + + .pl-xl-0, +.px-xl-0 { + padding-left: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .pt-xl-1, +.py-xl-1 { + padding-top: 0.25rem !important; + } + + .pr-xl-1, +.px-xl-1 { + padding-right: 0.25rem !important; + } + + .pb-xl-1, +.py-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pl-xl-1, +.px-xl-1 { + padding-left: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .pt-xl-2, +.py-xl-2 { + padding-top: 0.5rem !important; + } + + .pr-xl-2, +.px-xl-2 { + padding-right: 0.5rem !important; + } + + .pb-xl-2, +.py-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pl-xl-2, +.px-xl-2 { + padding-left: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .pt-xl-3, +.py-xl-3 { + padding-top: 1rem !important; + } + + .pr-xl-3, +.px-xl-3 { + padding-right: 1rem !important; + } + + .pb-xl-3, +.py-xl-3 { + padding-bottom: 1rem !important; + } + + .pl-xl-3, +.px-xl-3 { + padding-left: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .pt-xl-4, +.py-xl-4 { + padding-top: 1.5rem !important; + } + + .pr-xl-4, +.px-xl-4 { + padding-right: 1.5rem !important; + } + + .pb-xl-4, +.py-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pl-xl-4, +.px-xl-4 { + padding-left: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .pt-xl-5, +.py-xl-5 { + padding-top: 3rem !important; + } + + .pr-xl-5, +.px-xl-5 { + padding-right: 3rem !important; + } + + .pb-xl-5, +.py-xl-5 { + padding-bottom: 3rem !important; + } + + .pl-xl-5, +.px-xl-5 { + padding-left: 3rem !important; + } + + .m-xl-n1 { + margin: -0.25rem !important; + } + + .mt-xl-n1, +.my-xl-n1 { + margin-top: -0.25rem !important; + } + + .mr-xl-n1, +.mx-xl-n1 { + margin-right: -0.25rem !important; + } + + .mb-xl-n1, +.my-xl-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-xl-n1, +.mx-xl-n1 { + margin-left: -0.25rem !important; + } + + .m-xl-n2 { + margin: -0.5rem !important; + } + + .mt-xl-n2, +.my-xl-n2 { + margin-top: -0.5rem !important; + } + + .mr-xl-n2, +.mx-xl-n2 { + margin-right: -0.5rem !important; + } + + .mb-xl-n2, +.my-xl-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-xl-n2, +.mx-xl-n2 { + margin-left: -0.5rem !important; + } + + .m-xl-n3 { + margin: -1rem !important; + } + + .mt-xl-n3, +.my-xl-n3 { + margin-top: -1rem !important; + } + + .mr-xl-n3, +.mx-xl-n3 { + margin-right: -1rem !important; + } + + .mb-xl-n3, +.my-xl-n3 { + margin-bottom: -1rem !important; + } + + .ml-xl-n3, +.mx-xl-n3 { + margin-left: -1rem !important; + } + + .m-xl-n4 { + margin: -1.5rem !important; + } + + .mt-xl-n4, +.my-xl-n4 { + margin-top: -1.5rem !important; + } + + .mr-xl-n4, +.mx-xl-n4 { + margin-right: -1.5rem !important; + } + + .mb-xl-n4, +.my-xl-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-xl-n4, +.mx-xl-n4 { + margin-left: -1.5rem !important; + } + + .m-xl-n5 { + margin: -3rem !important; + } + + .mt-xl-n5, +.my-xl-n5 { + margin-top: -3rem !important; + } + + .mr-xl-n5, +.mx-xl-n5 { + margin-right: -3rem !important; + } + + .mb-xl-n5, +.my-xl-n5 { + margin-bottom: -3rem !important; + } + + .ml-xl-n5, +.mx-xl-n5 { + margin-left: -3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mt-xl-auto, +.my-xl-auto { + margin-top: auto !important; + } + + .mr-xl-auto, +.mx-xl-auto { + margin-right: auto !important; + } + + .mb-xl-auto, +.my-xl-auto { + margin-bottom: auto !important; + } + + .ml-xl-auto, +.mx-xl-auto { + margin-left: auto !important; + } +} +.stretched-link::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: rgba(0, 0, 0, 0); +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; +} + +.text-justify { + text-align: justify !important; +} + +.text-wrap { + white-space: normal !important; +} + +.text-nowrap { + white-space: nowrap !important; +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; + } + + .text-sm-right { + text-align: right !important; + } + + .text-sm-center { + text-align: center !important; + } +} +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; + } + + .text-md-right { + text-align: right !important; + } + + .text-md-center { + text-align: center !important; + } +} +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; + } + + .text-lg-right { + text-align: right !important; + } + + .text-lg-center { + text-align: center !important; + } +} +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; + } + + .text-xl-right { + text-align: right !important; + } + + .text-xl-center { + text-align: center !important; + } +} +.text-lowercase { + text-transform: lowercase !important; +} + +.text-uppercase { + text-transform: uppercase !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-lighter { + font-weight: lighter !important; +} + +.font-weight-normal { + font-weight: 400 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.font-weight-bolder { + font-weight: bolder !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-white { + color: #fff !important; +} + +.text-primary { + color: #5942c1 !important; +} + +a.text-primary:hover, a.text-primary:focus { + color: #3e2d89 !important; +} + +.text-secondary { + color: #ffe500 !important; +} + +a.text-secondary:hover, a.text-secondary:focus { + color: #b3a000 !important; +} + +.text-success { + color: #28a745 !important; +} + +a.text-success:hover, a.text-success:focus { + color: #19692c !important; +} + +.text-info { + color: #17a2b8 !important; +} + +a.text-info:hover, a.text-info:focus { + color: #0f6674 !important; +} + +.text-warning { + color: #ffe500 !important; +} + +a.text-warning:hover, a.text-warning:focus { + color: #b3a000 !important; +} + +.text-danger { + color: #dc3545 !important; +} + +a.text-danger:hover, a.text-danger:focus { + color: #a71d2a !important; +} + +.text-light { + color: #f8f9fa !important; +} + +a.text-light:hover, a.text-light:focus { + color: #cbd3da !important; +} + +.text-dark { + color: #343a40 !important; +} + +a.text-dark:hover, a.text-dark:focus { + color: #121416 !important; +} + +.text-gray { + color: #9c9e9f !important; +} + +a.text-gray:hover, a.text-gray:focus { + color: #757879 !important; +} + +.text-darker { + color: #212529 !important; +} + +a.text-darker:hover, a.text-darker:focus { + color: black !important; +} + +.text-body { + color: #212529 !important; +} + +.text-muted { + color: #6c757d !important; +} + +.text-black-50 { + color: rgba(0, 0, 0, 0.5) !important; +} + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.text-decoration-none { + text-decoration: none !important; +} + +.text-break { + word-break: break-word !important; + word-wrap: break-word !important; +} + +.text-reset { + color: inherit !important; +} + +.visible { + visibility: visible !important; +} + +.invisible { + visibility: hidden !important; +} + +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #5942c1; + --pink: #e83e8c; + --red: #dc3545; + --orange: #fd7e14; + --yellow: #ffe500; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #5942c1; + --secondary: #ffe500; + --success: #28a745; + --info: #17a2b8; + --warning: #ffe500; + --danger: #dc3545; + --light: #f8f9fa; + --dark: #343a40; + --gray: #9c9e9f; + --darker: #212529; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: "Exo 2", BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; +} + +h1, .h1 { + font-size: 3rem; +} +@media (max-width: 1200px) { + h1, .h1 { + font-size: calc(1.425rem + 2.1vw); + } +} + +h2, .h2 { + font-size: 2rem; +} +@media (max-width: 1200px) { + h2, .h2 { + font-size: calc(1.325rem + 0.9vw); + } +} + +h3, .h3 { + font-size: 1.75rem; +} +@media (max-width: 1200px) { + h3, .h3 { + font-size: calc(1.3rem + 0.6vw); + } +} + +h4, .h4 { + font-size: 1.5rem; +} +@media (max-width: 1200px) { + h4, .h4 { + font-size: calc(1.275rem + 0.3vw); + } +} + +h5, .h5 { + font-size: 1.25rem; +} + +h6, .h6 { + font-size: 1rem; +} + +.lead { + font-size: 1.25rem; + font-weight: 400; +} + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2; +} +@media (max-width: 1200px) { + .display-1 { + font-size: calc(1.725rem + 5.7vw); + } +} + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2; +} +@media (max-width: 1200px) { + .display-2 { + font-size: calc(1.675rem + 5.1vw); + } +} + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2; +} +@media (max-width: 1200px) { + .display-3 { + font-size: calc(1.575rem + 3.9vw); + } +} + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; +} +@media (max-width: 1200px) { + .display-4 { + font-size: calc(1.475rem + 2.7vw); + } +} + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +small, +.small { + font-size: 80%; + font-weight: 400; +} + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline-item { + display: inline-block; +} +.list-inline-item:not(:last-child) { + margin-right: 0.5rem; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d; +} +.blockquote-footer::before { + content: "— "; +} + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.3rem; +} +.card > hr { + margin-right: 0; + margin-left: 0; +} +.card > .list-group { + border-top: inherit; + border-bottom: inherit; +} +.card > .list-group:first-child { + border-top-width: 0; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} +.card > .list-group:last-child { + border-bottom-width: 0; + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); +} +.card > .card-header + .list-group, +.card > .list-group + .card-footer { + border-top: 0; +} + +.card-body { + flex: 1 1 auto; + min-height: 1px; + padding: 1.25rem; +} + +.card-title { + margin-bottom: 0.75rem; +} + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link:hover { + text-decoration: none; +} +.card-link + .card-link { + margin-left: 1.25rem; +} + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); +} +.card-header:first-child { + border-radius: calc(0.3rem - 1px) calc(0.3rem - 1px) 0 0; +} + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: rgba(0, 0, 0, 0.03); + border-top: 1px solid rgba(0, 0, 0, 0.125); +} +.card-footer:last-child { + border-radius: 0 0 calc(0.3rem - 1px) calc(0.3rem - 1px); +} + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; + border-radius: calc(0.3rem - 1px); +} + +.card-img, +.card-img-top, +.card-img-bottom { + flex-shrink: 0; + width: 100%; +} + +.card-img, +.card-img-top { + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} + +.card-img, +.card-img-bottom { + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); +} + +.card-deck .card { + margin-bottom: 10px; +} +@media (min-width: 576px) { + .card-deck { + display: flex; + flex-flow: row wrap; + margin-right: -10px; + margin-left: -10px; + } + .card-deck .card { + flex: 1 0 0%; + margin-right: 10px; + margin-bottom: 0; + margin-left: 10px; + } +} + +.card-group > .card { + margin-bottom: 10px; +} +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; + } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; + } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-top, +.card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-bottom, +.card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; + } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-top, +.card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-bottom, +.card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; + } +} + +.card-columns .card { + margin-bottom: 0.75rem; +} +@media (min-width: 576px) { + .card-columns { + column-count: 3; + column-gap: 1.25rem; + orphans: 1; + widows: 1; + } + .card-columns .card { + display: inline-block; + width: 100%; + } +} + +.accordion { + overflow-anchor: none; +} +.accordion > .card { + overflow: hidden; +} +.accordion > .card:not(:last-of-type) { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.accordion > .card:not(:first-of-type) { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.accordion > .card > .card-header { + border-radius: 0; + margin-bottom: -1px; +} + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.3rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .badge { + transition: none; + } +} +a.badge:hover, a.badge:focus { + text-decoration: none; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; +} + +.badge-primary { + color: #fff; + background-color: #5942c1; +} +a.badge-primary:hover, a.badge-primary:focus { + color: #fff; + background-color: #46339d; +} +a.badge-primary:focus, a.badge-primary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.5); +} + +.badge-secondary { + color: #212529; + background-color: #ffe500; +} +a.badge-secondary:hover, a.badge-secondary:focus { + color: #212529; + background-color: #ccb700; +} +a.badge-secondary:focus, a.badge-secondary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 229, 0, 0.5); +} + +.badge-success { + color: #fff; + background-color: #28a745; +} +a.badge-success:hover, a.badge-success:focus { + color: #fff; + background-color: #1e7e34; +} +a.badge-success:focus, a.badge-success.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.badge-info { + color: #fff; + background-color: #17a2b8; +} +a.badge-info:hover, a.badge-info:focus { + color: #fff; + background-color: #117a8b; +} +a.badge-info:focus, a.badge-info.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.badge-warning { + color: #212529; + background-color: #ffe500; +} +a.badge-warning:hover, a.badge-warning:focus { + color: #212529; + background-color: #ccb700; +} +a.badge-warning:focus, a.badge-warning.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 229, 0, 0.5); +} + +.badge-danger { + color: #fff; + background-color: #dc3545; +} +a.badge-danger:hover, a.badge-danger:focus { + color: #fff; + background-color: #bd2130; +} +a.badge-danger:focus, a.badge-danger.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.badge-light { + color: #212529; + background-color: #f8f9fa; +} +a.badge-light:hover, a.badge-light:focus { + color: #212529; + background-color: #dae0e5; +} +a.badge-light:focus, a.badge-light.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.badge-dark { + color: #fff; + background-color: #343a40; +} +a.badge-dark:hover, a.badge-dark:focus { + color: #fff; + background-color: #1d2124; +} +a.badge-dark:focus, a.badge-dark.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.badge-gray { + color: #212529; + background-color: #9c9e9f; +} +a.badge-gray:hover, a.badge-gray:focus { + color: #212529; + background-color: #828586; +} +a.badge-gray:focus, a.badge-gray.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(156, 158, 159, 0.5); +} + +.badge-darker { + color: #fff; + background-color: #212529; +} +a.badge-darker:hover, a.badge-darker:focus { + color: #fff; + background-color: #0a0c0d; +} +a.badge-darker:focus, a.badge-darker.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(33, 37, 41, 0.5); +} + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + 1.5rem + 2px); + padding: 0.75rem 1.2rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.3rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; + } +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; +} +.form-control:focus { + color: #495057; + background-color: #fff; + border-color: #ada2e0; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.form-control::placeholder { + color: #6c757d; + opacity: 1; +} +.form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1; +} + +input[type=date].form-control, +input[type=time].form-control, +input[type=datetime-local].form-control, +input[type=month].form-control { + appearance: none; +} + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.form-control-file, +.form-control-range { + display: block; + width: 100%; +} + +.col-form-label { + padding-top: calc(0.75rem + 1px); + padding-bottom: calc(0.75rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; +} + +.form-control-plaintext { + display: block; + width: 100%; + padding: 0.75rem 0; + margin-bottom: 0; + font-size: 1rem; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; +} +.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm { + height: calc(1.5em + 0.5rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +select.form-control[size], select.form-control[multiple] { + height: auto; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-text { + display: block; + margin-top: 0.25rem; +} + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; +} +.form-row > .col, +.form-row > [class*=col-] { + padding-right: 5px; + padding-left: 5px; +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; +} + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; +} +.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { + color: #6c757d; +} + +.form-check-label { + margin-bottom: 0; +} + +.form-check-inline { + display: inline-flex; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; +} +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #28a745; +} + +.valid-tooltip { + position: absolute; + top: 100%; + left: 0; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(40, 167, 69, 0.9); + border-radius: 0.3rem; +} +.form-row > .col > .valid-tooltip, .form-row > [class*=col-] > .valid-tooltip { + left: 5px; +} + +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-control:valid, .form-control.is-valid { + border-color: #28a745; + padding-right: calc(1.5em + 1.5rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.375rem) center; + background-size: calc(0.75em + 0.75rem) calc(0.75em + 0.75rem); +} +.was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + 1.5rem); + background-position: top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem); +} + +.was-validated .custom-select:valid, .custom-select.is-valid { + border-color: #28a745; + padding-right: calc(0.75em + 3.325rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1.2rem center/8px 10px no-repeat, #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 2.2rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem) no-repeat; +} +.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #28a745; +} +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #28a745; +} +.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + border-color: #28a745; +} +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + border-color: #34ce57; + background-color: #34ce57; +} +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} +.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #28a745; +} + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #28a745; +} +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +} + +.invalid-tooltip { + position: absolute; + top: 100%; + left: 0; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(220, 53, 69, 0.9); + border-radius: 0.3rem; +} +.form-row > .col > .invalid-tooltip, .form-row > [class*=col-] > .invalid-tooltip { + left: 5px; +} + +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: #dc3545; + padding-right: calc(1.5em + 1.5rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.375rem) center; + background-size: calc(0.75em + 0.75rem) calc(0.75em + 0.75rem); +} +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + 1.5rem); + background-position: top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem); +} + +.was-validated .custom-select:invalid, .custom-select.is-invalid { + border-color: #dc3545; + padding-right: calc(0.75em + 3.325rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1.2rem center/8px 10px no-repeat, #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 2.2rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem) no-repeat; +} +.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; +} +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #dc3545; +} +.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + border-color: #dc3545; +} +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + border-color: #e4606d; + background-color: #e4606d; +} +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} +.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #dc3545; +} + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #dc3545; +} +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.form-inline { + display: flex; + flex-flow: row wrap; + align-items: center; +} +.form-inline .form-check { + width: 100%; +} +@media (min-width: 576px) { + .form-inline label { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0; + } + .form-inline .form-group { + display: flex; + flex: 0 0 auto; + flex-flow: row wrap; + align-items: center; + margin-bottom: 0; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-plaintext { + display: inline-block; + } + .form-inline .input-group, +.form-inline .custom-select { + width: auto; + } + .form-inline .form-check { + display: flex; + align-items: center; + justify-content: center; + width: auto; + padding-left: 0; + } + .form-inline .form-check-input { + position: relative; + flex-shrink: 0; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; + } + .form-inline .custom-control { + align-items: center; + justify-content: center; + } + .form-inline .custom-control-label { + margin-bottom: 0; + } +} + +.custom-control { + position: relative; + z-index: 1; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; + color-adjust: exact; +} + +.custom-control-inline { + display: inline-flex; + margin-right: 1rem; +} + +.custom-control-input { + position: absolute; + left: 0; + z-index: -1; + width: 1rem; + height: 1.25rem; + opacity: 0; +} +.custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #5942c1; + background-color: #5942c1; +} +.custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #ada2e0; +} +.custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #cfc8ed; + border-color: #cfc8ed; +} +.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { + color: #6c757d; +} +.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { + background-color: #e9ecef; +} + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top; +} +.custom-control-label::before { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; +} +.custom-control-label::after { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: 50%/50% 50% no-repeat; +} + +.custom-checkbox .custom-control-label::before { + border-radius: 0.3rem; +} +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); +} +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #5942c1; + background-color: #5942c1; +} +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); +} +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(89, 66, 193, 0.5); +} +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(89, 66, 193, 0.5); +} + +.custom-radio .custom-control-label::before { + border-radius: 50%; +} +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); +} +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(89, 66, 193, 0.5); +} + +.custom-switch { + padding-left: 2.25rem; +} +.custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; +} +.custom-switch .custom-control-label::after { + top: calc(0.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: 0.5rem; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none; + } +} +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + transform: translateX(0.75rem); +} +.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(89, 66, 193, 0.5); +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + 1.5rem + 2px); + padding: 0.75rem 2.2rem 0.75rem 1.2rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1.2rem center/8px 10px no-repeat; + border: 1px solid #ced4da; + border-radius: 0.3rem; + appearance: none; +} +.custom-select:focus { + border-color: #ada2e0; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.custom-select:focus::-ms-value { + color: #495057; + background-color: #fff; +} +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 1.2rem; + background-image: none; +} +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; +} +.custom-select::-ms-expand { + display: none; +} +.custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; +} + +.custom-select-sm { + height: calc(1.5em + 0.5rem + 2px); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; +} + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(1.5em + 1.5rem + 2px); + margin-bottom: 0; +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(1.5em + 1.5rem + 2px); + margin: 0; + overflow: hidden; + opacity: 0; +} +.custom-file-input:focus ~ .custom-file-label { + border-color: #ada2e0; + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.custom-file-input[disabled] ~ .custom-file-label, .custom-file-input:disabled ~ .custom-file-label { + background-color: #e9ecef; +} +.custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; +} +.custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse); +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(1.5em + 1.5rem + 2px); + padding: 0.75rem 1.2rem; + overflow: hidden; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.3rem; +} +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + 1.5rem); + padding: 0.75rem 1.2rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 0.3rem 0.3rem 0; +} + +.custom-range { + width: 100%; + height: 1.4rem; + padding: 0; + background-color: transparent; + appearance: none; +} +.custom-range:focus { + outline: 0; +} +.custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.custom-range::-moz-focus-outer { + border: 0; +} +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #5942c1; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} +@media (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + transition: none; + } +} +.custom-range::-webkit-slider-thumb:active { + background-color: #cfc8ed; +} +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; +} +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #5942c1; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} +@media (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + transition: none; + } +} +.custom-range::-moz-range-thumb:active { + background-color: #cfc8ed; +} +.custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; +} +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0.2rem; + margin-left: 0.2rem; + background-color: #5942c1; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} +@media (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + transition: none; + } +} +.custom-range::-ms-thumb:active { + background-color: #cfc8ed; +} +.custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; +} +.custom-range::-ms-fill-lower { + background-color: #dee2e6; + border-radius: 1rem; +} +.custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dee2e6; + border-radius: 1rem; +} +.custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd; +} +.custom-range:disabled::-webkit-slider-runnable-track { + cursor: default; +} +.custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd; +} +.custom-range:disabled::-moz-range-track { + cursor: default; +} +.custom-range:disabled::-ms-thumb { + background-color: #adb5bd; +} + +.custom-control-label::before, +.custom-file-label, +.custom-select { + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .custom-control-label::before, +.custom-file-label, +.custom-select { + transition: none; + } +} + +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: 0.3rem; +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit; +} +.list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa; +} +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef; +} + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} +.list-group-item:first-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} +.list-group-item:last-child { + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; +} +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff; +} +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #5942c1; + border-color: #5942c1; +} +.list-group-item + .list-group-item { + border-top-width: 0; +} +.list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; +} + +.list-group-horizontal { + flex-direction: row; +} +.list-group-horizontal > .list-group-item:first-child { + border-bottom-left-radius: 0.3rem; + border-top-right-radius: 0; +} +.list-group-horizontal > .list-group-item:last-child { + border-top-right-radius: 0.3rem; + border-bottom-left-radius: 0; +} +.list-group-horizontal > .list-group-item.active { + margin-top: 0; +} +.list-group-horizontal > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; +} +.list-group-horizontal > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + flex-direction: row; + } + .list-group-horizontal-sm > .list-group-item:first-child { + border-bottom-left-radius: 0.3rem; + border-top-right-radius: 0; + } + .list-group-horizontal-sm > .list-group-item:last-child { + border-top-right-radius: 0.3rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 768px) { + .list-group-horizontal-md { + flex-direction: row; + } + .list-group-horizontal-md > .list-group-item:first-child { + border-bottom-left-radius: 0.3rem; + border-top-right-radius: 0; + } + .list-group-horizontal-md > .list-group-item:last-child { + border-top-right-radius: 0.3rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 992px) { + .list-group-horizontal-lg { + flex-direction: row; + } + .list-group-horizontal-lg > .list-group-item:first-child { + border-bottom-left-radius: 0.3rem; + border-top-right-radius: 0; + } + .list-group-horizontal-lg > .list-group-item:last-child { + border-top-right-radius: 0.3rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 1200px) { + .list-group-horizontal-xl { + flex-direction: row; + } + .list-group-horizontal-xl > .list-group-item:first-child { + border-bottom-left-radius: 0.3rem; + border-top-right-radius: 0; + } + .list-group-horizontal-xl > .list-group-item:last-child { + border-top-right-radius: 0.3rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +.list-group-flush { + border-radius: 0; +} +.list-group-flush > .list-group-item { + border-width: 0 0 1px; +} +.list-group-flush > .list-group-item:last-child { + border-bottom-width: 0; +} + +.list-group-item-primary { + color: #2e2264; + background-color: #d1caee; +} +.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #2e2264; + background-color: #c0b7e8; +} +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #2e2264; + border-color: #2e2264; +} + +.list-group-item-secondary { + color: #857700; + background-color: #fff8b8; +} +.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #857700; + background-color: #fff59f; +} +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #857700; + border-color: #857700; +} + +.list-group-item-success { + color: #155724; + background-color: #c3e6cb; +} +.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #155724; + background-color: #b1dfbb; +} +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #155724; + border-color: #155724; +} + +.list-group-item-info { + color: #0c5460; + background-color: #bee5eb; +} +.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #0c5460; + background-color: #abdde5; +} +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0c5460; + border-color: #0c5460; +} + +.list-group-item-warning { + color: #857700; + background-color: #fff8b8; +} +.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #857700; + background-color: #fff59f; +} +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #857700; + border-color: #857700; +} + +.list-group-item-danger { + color: #721c24; + background-color: #f5c6cb; +} +.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #721c24; + background-color: #f1b0b7; +} +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #721c24; + border-color: #721c24; +} + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe; +} +.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818182; + background-color: #ececf6; +} +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182; +} + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca; +} +.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #1b1e21; + background-color: #b9bbbe; +} +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21; +} + +.list-group-item-gray { + color: #515253; + background-color: #e3e4e4; +} +.list-group-item-gray.list-group-item-action:hover, .list-group-item-gray.list-group-item-action:focus { + color: #515253; + background-color: #d6d7d7; +} +.list-group-item-gray.list-group-item-action.active { + color: #fff; + background-color: #515253; + border-color: #515253; +} + +.list-group-item-darker { + color: #111315; + background-color: #c1c2c3; +} +.list-group-item-darker.list-group-item-action:hover, .list-group-item-darker.list-group-item-action:focus { + color: #111315; + background-color: #b4b5b6; +} +.list-group-item-darker.list-group-item-action.active { + color: #fff; + background-color: #111315; + border-color: #111315; +} + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.3rem; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 700; +} + +.alert-dismissible { + padding-right: 4rem; +} +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + z-index: 2; + padding: 0.75rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #2e2264; + background-color: #ded9f3; + border-color: #d1caee; +} +.alert-primary hr { + border-top-color: #c0b7e8; +} +.alert-primary .alert-link { + color: #1c153e; +} + +.alert-secondary { + color: #857700; + background-color: #fffacc; + border-color: #fff8b8; +} +.alert-secondary hr { + border-top-color: #fff59f; +} +.alert-secondary .alert-link { + color: #524900; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} +.alert-success hr { + border-top-color: #b1dfbb; +} +.alert-success .alert-link { + color: #0b2e13; +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; +} +.alert-info hr { + border-top-color: #abdde5; +} +.alert-info .alert-link { + color: #062c33; +} + +.alert-warning { + color: #857700; + background-color: #fffacc; + border-color: #fff8b8; +} +.alert-warning hr { + border-top-color: #fff59f; +} +.alert-warning .alert-link { + color: #524900; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} +.alert-danger hr { + border-top-color: #f1b0b7; +} +.alert-danger .alert-link { + color: #491217; +} + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; +} +.alert-light hr { + border-top-color: #ececf6; +} +.alert-light .alert-link { + color: #686868; +} + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; +} +.alert-dark hr { + border-top-color: #b9bbbe; +} +.alert-dark .alert-link { + color: #040505; +} + +.alert-gray { + color: #515253; + background-color: #ebecec; + border-color: #e3e4e4; +} +.alert-gray hr { + border-top-color: #d6d7d7; +} +.alert-gray .alert-link { + color: #383939; +} + +.alert-darker { + color: #111315; + background-color: #d3d3d4; + border-color: #c1c2c3; +} +.alert-darker hr { + border-top-color: #b4b5b6; +} +.alert-darker .alert-link { + color: black; +} + +.container, +.container-fluid, +.container-xl, +.container-lg, +.container-md, +.container-sm { + width: 100%; + padding-right: 10px; + padding-left: 10px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container-sm, .container { + max-width: 540px; + } +} +@media (min-width: 768px) { + .container-md, .container-sm, .container { + max-width: 720px; + } +} +@media (min-width: 992px) { + .container-lg, .container-md, .container-sm, .container { + max-width: 960px; + } +} +@media (min-width: 1200px) { + .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1500px; + } +} +.row { + display: flex; + flex-wrap: wrap; + margin-right: -10px; + margin-left: -10px; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} +.no-gutters > .col, +.no-gutters > [class*=col-] { + padding-right: 0; + padding-left: 0; +} + +.col-xl, +.col-xl-auto, .col-xl-12, .col-xl-11, .col-xl-10, .col-xl-9, .col-xl-8, .col-xl-7, .col-xl-6, .col-xl-5, .col-xl-4, .col-xl-3, .col-xl-2, .col-xl-1, .col-lg, +.col-lg-auto, .col-lg-12, .col-lg-11, .col-lg-10, .col-lg-9, .col-lg-8, .col-lg-7, .col-lg-6, .col-lg-5, .col-lg-4, .col-lg-3, .col-lg-2, .col-lg-1, .col-md, +.col-md-auto, .col-md-12, .col-md-11, .col-md-10, .col-md-9, .col-md-8, .col-md-7, .col-md-6, .col-md-5, .col-md-4, .col-md-3, .col-md-2, .col-md-1, .col-sm, +.col-sm-auto, .col-sm-12, .col-sm-11, .col-sm-10, .col-sm-9, .col-sm-8, .col-sm-7, .col-sm-6, .col-sm-5, .col-sm-4, .col-sm-3, .col-sm-2, .col-sm-1, .col, +.col-auto, .col-12, .col-11, .col-10, .col-9, .col-8, .col-7, .col-6, .col-5, .col-4, .col-3, .col-2, .col-1 { + position: relative; + width: 100%; + padding-right: 10px; + padding-left: 10px; +} + +.col { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; +} + +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; +} + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; +} + +.row-cols-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; +} + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; +} + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; +} + +.row-cols-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; +} + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; +} + +.col-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; +} + +.col-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; +} + +.col-3 { + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; +} + +.col-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; +} + +.col-6 { + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; +} + +.col-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; +} + +.col-9 { + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; +} + +.col-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; +} + +.col-12 { + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + order: -1; +} + +.order-last { + order: 13; +} + +.order-0 { + order: 0; +} + +.order-1 { + order: 1; +} + +.order-2 { + order: 2; +} + +.order-3 { + order: 3; +} + +.order-4 { + order: 4; +} + +.order-5 { + order: 5; +} + +.order-6 { + order: 6; +} + +.order-7 { + order: 7; +} + +.order-8 { + order: 8; +} + +.order-9 { + order: 9; +} + +.order-10 { + order: 10; +} + +.order-11 { + order: 11; +} + +.order-12 { + order: 12; +} + +.offset-1 { + margin-left: 8.3333333333%; +} + +.offset-2 { + margin-left: 16.6666666667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.3333333333%; +} + +.offset-5 { + margin-left: 41.6666666667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.3333333333%; +} + +.offset-8 { + margin-left: 66.6666666667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.3333333333%; +} + +.offset-11 { + margin-left: 91.6666666667%; +} + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; + } + + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; + } + + .row-cols-sm-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; + } + + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; + } + + .row-cols-sm-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-sm-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; + } + + .col-sm-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-sm-3 { + flex: 0 0 25%; + max-width: 25%; + } + + .col-sm-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .col-sm-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; + } + + .col-sm-6 { + flex: 0 0 50%; + max-width: 50%; + } + + .col-sm-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; + } + + .col-sm-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; + } + + .col-sm-9 { + flex: 0 0 75%; + max-width: 75%; + } + + .col-sm-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; + } + + .col-sm-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; + } + + .col-sm-12 { + flex: 0 0 100%; + max-width: 100%; + } + + .order-sm-first { + order: -1; + } + + .order-sm-last { + order: 13; + } + + .order-sm-0 { + order: 0; + } + + .order-sm-1 { + order: 1; + } + + .order-sm-2 { + order: 2; + } + + .order-sm-3 { + order: 3; + } + + .order-sm-4 { + order: 4; + } + + .order-sm-5 { + order: 5; + } + + .order-sm-6 { + order: 6; + } + + .order-sm-7 { + order: 7; + } + + .order-sm-8 { + order: 8; + } + + .order-sm-9 { + order: 9; + } + + .order-sm-10 { + order: 10; + } + + .order-sm-11 { + order: 11; + } + + .order-sm-12 { + order: 12; + } + + .offset-sm-0 { + margin-left: 0; + } + + .offset-sm-1 { + margin-left: 8.3333333333%; + } + + .offset-sm-2 { + margin-left: 16.6666666667%; + } + + .offset-sm-3 { + margin-left: 25%; + } + + .offset-sm-4 { + margin-left: 33.3333333333%; + } + + .offset-sm-5 { + margin-left: 41.6666666667%; + } + + .offset-sm-6 { + margin-left: 50%; + } + + .offset-sm-7 { + margin-left: 58.3333333333%; + } + + .offset-sm-8 { + margin-left: 66.6666666667%; + } + + .offset-sm-9 { + margin-left: 75%; + } + + .offset-sm-10 { + margin-left: 83.3333333333%; + } + + .offset-sm-11 { + margin-left: 91.6666666667%; + } +} +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; + } + + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; + } + + .row-cols-md-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; + } + + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; + } + + .row-cols-md-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-md-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; + } + + .col-md-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-md-3 { + flex: 0 0 25%; + max-width: 25%; + } + + .col-md-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .col-md-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; + } + + .col-md-6 { + flex: 0 0 50%; + max-width: 50%; + } + + .col-md-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; + } + + .col-md-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; + } + + .col-md-9 { + flex: 0 0 75%; + max-width: 75%; + } + + .col-md-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; + } + + .col-md-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; + } + + .col-md-12 { + flex: 0 0 100%; + max-width: 100%; + } + + .order-md-first { + order: -1; + } + + .order-md-last { + order: 13; + } + + .order-md-0 { + order: 0; + } + + .order-md-1 { + order: 1; + } + + .order-md-2 { + order: 2; + } + + .order-md-3 { + order: 3; + } + + .order-md-4 { + order: 4; + } + + .order-md-5 { + order: 5; + } + + .order-md-6 { + order: 6; + } + + .order-md-7 { + order: 7; + } + + .order-md-8 { + order: 8; + } + + .order-md-9 { + order: 9; + } + + .order-md-10 { + order: 10; + } + + .order-md-11 { + order: 11; + } + + .order-md-12 { + order: 12; + } + + .offset-md-0 { + margin-left: 0; + } + + .offset-md-1 { + margin-left: 8.3333333333%; + } + + .offset-md-2 { + margin-left: 16.6666666667%; + } + + .offset-md-3 { + margin-left: 25%; + } + + .offset-md-4 { + margin-left: 33.3333333333%; + } + + .offset-md-5 { + margin-left: 41.6666666667%; + } + + .offset-md-6 { + margin-left: 50%; + } + + .offset-md-7 { + margin-left: 58.3333333333%; + } + + .offset-md-8 { + margin-left: 66.6666666667%; + } + + .offset-md-9 { + margin-left: 75%; + } + + .offset-md-10 { + margin-left: 83.3333333333%; + } + + .offset-md-11 { + margin-left: 91.6666666667%; + } +} +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; + } + + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; + } + + .row-cols-lg-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; + } + + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; + } + + .row-cols-lg-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-lg-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; + } + + .col-lg-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-lg-3 { + flex: 0 0 25%; + max-width: 25%; + } + + .col-lg-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .col-lg-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; + } + + .col-lg-6 { + flex: 0 0 50%; + max-width: 50%; + } + + .col-lg-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; + } + + .col-lg-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; + } + + .col-lg-9 { + flex: 0 0 75%; + max-width: 75%; + } + + .col-lg-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; + } + + .col-lg-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; + } + + .col-lg-12 { + flex: 0 0 100%; + max-width: 100%; + } + + .order-lg-first { + order: -1; + } + + .order-lg-last { + order: 13; + } + + .order-lg-0 { + order: 0; + } + + .order-lg-1 { + order: 1; + } + + .order-lg-2 { + order: 2; + } + + .order-lg-3 { + order: 3; + } + + .order-lg-4 { + order: 4; + } + + .order-lg-5 { + order: 5; + } + + .order-lg-6 { + order: 6; + } + + .order-lg-7 { + order: 7; + } + + .order-lg-8 { + order: 8; + } + + .order-lg-9 { + order: 9; + } + + .order-lg-10 { + order: 10; + } + + .order-lg-11 { + order: 11; + } + + .order-lg-12 { + order: 12; + } + + .offset-lg-0 { + margin-left: 0; + } + + .offset-lg-1 { + margin-left: 8.3333333333%; + } + + .offset-lg-2 { + margin-left: 16.6666666667%; + } + + .offset-lg-3 { + margin-left: 25%; + } + + .offset-lg-4 { + margin-left: 33.3333333333%; + } + + .offset-lg-5 { + margin-left: 41.6666666667%; + } + + .offset-lg-6 { + margin-left: 50%; + } + + .offset-lg-7 { + margin-left: 58.3333333333%; + } + + .offset-lg-8 { + margin-left: 66.6666666667%; + } + + .offset-lg-9 { + margin-left: 75%; + } + + .offset-lg-10 { + margin-left: 83.3333333333%; + } + + .offset-lg-11 { + margin-left: 91.6666666667%; + } +} +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; + } + + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; + } + + .row-cols-xl-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; + } + + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; + } + + .row-cols-xl-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-xl-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; + } + + .col-xl-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; + } + + .col-xl-3 { + flex: 0 0 25%; + max-width: 25%; + } + + .col-xl-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; + } + + .col-xl-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; + } + + .col-xl-6 { + flex: 0 0 50%; + max-width: 50%; + } + + .col-xl-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; + } + + .col-xl-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; + } + + .col-xl-9 { + flex: 0 0 75%; + max-width: 75%; + } + + .col-xl-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; + } + + .col-xl-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; + } + + .col-xl-12 { + flex: 0 0 100%; + max-width: 100%; + } + + .order-xl-first { + order: -1; + } + + .order-xl-last { + order: 13; + } + + .order-xl-0 { + order: 0; + } + + .order-xl-1 { + order: 1; + } + + .order-xl-2 { + order: 2; + } + + .order-xl-3 { + order: 3; + } + + .order-xl-4 { + order: 4; + } + + .order-xl-5 { + order: 5; + } + + .order-xl-6 { + order: 6; + } + + .order-xl-7 { + order: 7; + } + + .order-xl-8 { + order: 8; + } + + .order-xl-9 { + order: 9; + } + + .order-xl-10 { + order: 10; + } + + .order-xl-11 { + order: 11; + } + + .order-xl-12 { + order: 12; + } + + .offset-xl-0 { + margin-left: 0; + } + + .offset-xl-1 { + margin-left: 8.3333333333%; + } + + .offset-xl-2 { + margin-left: 16.6666666667%; + } + + .offset-xl-3 { + margin-left: 25%; + } + + .offset-xl-4 { + margin-left: 33.3333333333%; + } + + .offset-xl-5 { + margin-left: 41.6666666667%; + } + + .offset-xl-6 { + margin-left: 50%; + } + + .offset-xl-7 { + margin-left: 58.3333333333%; + } + + .offset-xl-8 { + margin-left: 66.6666666667%; + } + + .offset-xl-9 { + margin-left: 75%; + } + + .offset-xl-10 { + margin-left: 83.3333333333%; + } + + .offset-xl-11 { + margin-left: 91.6666666667%; + } +} +.fade { + transition: opacity 0.15s linear; +} +@media (prefers-reduced-motion: reduce) { + .fade { + transition: none; + } +} +.fade:not(.show) { + opacity: 0; +} + +.collapse:not(.show) { + display: none; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; +} +@media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; + } +} + +.btn { + display: inline-block; + font-weight: 400; + color: #212529; + text-align: center; + vertical-align: middle; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.75rem 1.2rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.3rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .btn { + transition: none; + } +} +.btn:hover { + color: #212529; + text-decoration: none; +} +.btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.25); +} +.btn.disabled, .btn:disabled { + opacity: 0.65; +} +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; +} + +.btn-primary { + color: #fff; + background-color: #5942c1; + border-color: #5942c1; +} +.btn-primary:hover { + color: #fff; + background-color: #4b37a6; + border-color: #46339d; +} +.btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #4b37a6; + border-color: #46339d; + box-shadow: 0 0 0 0.2rem rgba(114, 94, 202, 0.5); +} +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #5942c1; + border-color: #5942c1; +} +.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, .show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #46339d; + border-color: #423093; +} +.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(114, 94, 202, 0.5); +} + +.btn-secondary { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-secondary:hover { + color: #212529; + background-color: #d9c300; + border-color: #ccb700; +} +.btn-secondary:focus, .btn-secondary.focus { + color: #212529; + background-color: #d9c300; + border-color: #ccb700; + box-shadow: 0 0 0 0.2rem rgba(222, 200, 6, 0.5); +} +.btn-secondary.disabled, .btn-secondary:disabled { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, .show > .btn-secondary.dropdown-toggle { + color: #212529; + background-color: #ccb700; + border-color: #bfac00; +} +.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 200, 6, 0.5); +} + +.btn-success { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} +.btn-success:hover { + color: #fff; + background-color: #218838; + border-color: #1e7e34; +} +.btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #218838; + border-color: #1e7e34; + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} +.btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} +.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, .show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #1e7e34; + border-color: #1c7430; +} +.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, .show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} + +.btn-info { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} +.btn-info:hover { + color: #fff; + background-color: #138496; + border-color: #117a8b; +} +.btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #138496; + border-color: #117a8b; + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} +.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, .show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #117a8b; + border-color: #10707f; +} +.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, .show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} + +.btn-warning { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-warning:hover { + color: #212529; + background-color: #d9c300; + border-color: #ccb700; +} +.btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #d9c300; + border-color: #ccb700; + box-shadow: 0 0 0 0.2rem rgba(222, 200, 6, 0.5); +} +.btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, .show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #ccb700; + border-color: #bfac00; +} +.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 200, 6, 0.5); +} + +.btn-danger { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} +.btn-danger:hover { + color: #fff; + background-color: #c82333; + border-color: #bd2130; +} +.btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #c82333; + border-color: #bd2130; + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} +.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, .show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #bd2130; + border-color: #b21f2d; +} +.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} +.btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; +} +.btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); +} +.btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} +.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, .show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df; +} +.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, .show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); +} + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} +.btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124; +} +.btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); +} +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} +.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, .show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d; +} +.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); +} + +.btn-gray { + color: #212529; + background-color: #9c9e9f; + border-color: #9c9e9f; +} +.btn-gray:hover { + color: #fff; + background-color: #898b8c; + border-color: #828586; +} +.btn-gray:focus, .btn-gray.focus { + color: #fff; + background-color: #898b8c; + border-color: #828586; + box-shadow: 0 0 0 0.2rem rgba(138, 140, 141, 0.5); +} +.btn-gray.disabled, .btn-gray:disabled { + color: #212529; + background-color: #9c9e9f; + border-color: #9c9e9f; +} +.btn-gray:not(:disabled):not(.disabled):active, .btn-gray:not(:disabled):not(.disabled).active, .show > .btn-gray.dropdown-toggle { + color: #fff; + background-color: #828586; + border-color: #7c7e80; +} +.btn-gray:not(:disabled):not(.disabled):active:focus, .btn-gray:not(:disabled):not(.disabled).active:focus, .show > .btn-gray.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(138, 140, 141, 0.5); +} + +.btn-darker { + color: #fff; + background-color: #212529; + border-color: #212529; +} +.btn-darker:hover { + color: #fff; + background-color: #101214; + border-color: #0a0c0d; +} +.btn-darker:focus, .btn-darker.focus { + color: #fff; + background-color: #101214; + border-color: #0a0c0d; + box-shadow: 0 0 0 0.2rem rgba(66, 70, 73, 0.5); +} +.btn-darker.disabled, .btn-darker:disabled { + color: #fff; + background-color: #212529; + border-color: #212529; +} +.btn-darker:not(:disabled):not(.disabled):active, .btn-darker:not(:disabled):not(.disabled).active, .show > .btn-darker.dropdown-toggle { + color: #fff; + background-color: #0a0c0d; + border-color: #050506; +} +.btn-darker:not(:disabled):not(.disabled):active:focus, .btn-darker:not(:disabled):not(.disabled).active:focus, .show > .btn-darker.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(66, 70, 73, 0.5); +} + +.btn-outline-primary { + color: #5942c1; + border-color: #5942c1; +} +.btn-outline-primary:hover { + color: #fff; + background-color: #5942c1; + border-color: #5942c1; +} +.btn-outline-primary:focus, .btn-outline-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.5); +} +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #5942c1; + background-color: transparent; +} +.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, .show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #5942c1; + border-color: #5942c1; +} +.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(89, 66, 193, 0.5); +} + +.btn-outline-secondary { + color: #ffe500; + border-color: #ffe500; +} +.btn-outline-secondary:hover { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-outline-secondary:focus, .btn-outline-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 229, 0, 0.5); +} +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #ffe500; + background-color: transparent; +} +.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, .show > .btn-outline-secondary.dropdown-toggle { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 229, 0, 0.5); +} + +.btn-outline-success { + color: #28a745; + border-color: #28a745; +} +.btn-outline-success:hover { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} +.btn-outline-success:focus, .btn-outline-success.focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #28a745; + background-color: transparent; +} +.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, .show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} +.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-info { + color: #17a2b8; + border-color: #17a2b8; +} +.btn-outline-info:hover { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} +.btn-outline-info:focus, .btn-outline-info.focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #17a2b8; + background-color: transparent; +} +.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, .show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} +.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-warning { + color: #ffe500; + border-color: #ffe500; +} +.btn-outline-warning:hover { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-outline-warning:focus, .btn-outline-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 229, 0, 0.5); +} +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffe500; + background-color: transparent; +} +.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, .show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffe500; + border-color: #ffe500; +} +.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 229, 0, 0.5); +} + +.btn-outline-danger { + color: #dc3545; + border-color: #dc3545; +} +.btn-outline-danger:hover { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} +.btn-outline-danger:focus, .btn-outline-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #dc3545; + background-color: transparent; +} +.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, .show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} +.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-light { + color: #f8f9fa; + border-color: #f8f9fa; +} +.btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} +.btn-outline-light:focus, .btn-outline-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent; +} +.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, .show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} +.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-dark { + color: #343a40; + border-color: #343a40; +} +.btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} +.btn-outline-dark:focus, .btn-outline-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent; +} +.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, .show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} +.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-outline-gray { + color: #9c9e9f; + border-color: #9c9e9f; +} +.btn-outline-gray:hover { + color: #212529; + background-color: #9c9e9f; + border-color: #9c9e9f; +} +.btn-outline-gray:focus, .btn-outline-gray.focus { + box-shadow: 0 0 0 0.2rem rgba(156, 158, 159, 0.5); +} +.btn-outline-gray.disabled, .btn-outline-gray:disabled { + color: #9c9e9f; + background-color: transparent; +} +.btn-outline-gray:not(:disabled):not(.disabled):active, .btn-outline-gray:not(:disabled):not(.disabled).active, .show > .btn-outline-gray.dropdown-toggle { + color: #212529; + background-color: #9c9e9f; + border-color: #9c9e9f; +} +.btn-outline-gray:not(:disabled):not(.disabled):active:focus, .btn-outline-gray:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-gray.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(156, 158, 159, 0.5); +} + +.btn-outline-darker { + color: #212529; + border-color: #212529; +} +.btn-outline-darker:hover { + color: #fff; + background-color: #212529; + border-color: #212529; +} +.btn-outline-darker:focus, .btn-outline-darker.focus { + box-shadow: 0 0 0 0.2rem rgba(33, 37, 41, 0.5); +} +.btn-outline-darker.disabled, .btn-outline-darker:disabled { + color: #212529; + background-color: transparent; +} +.btn-outline-darker:not(:disabled):not(.disabled):active, .btn-outline-darker:not(:disabled):not(.disabled).active, .show > .btn-outline-darker.dropdown-toggle { + color: #fff; + background-color: #212529; + border-color: #212529; +} +.btn-outline-darker:not(:disabled):not(.disabled):active:focus, .btn-outline-darker:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-darker.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(33, 37, 41, 0.5); +} + +.btn-link { + font-weight: 400; + color: #5942c1; + text-decoration: none; +} +.btn-link:hover { + color: #3e2d89; + text-decoration: underline; +} +.btn-link:focus, .btn-link.focus { + text-decoration: underline; +} +.btn-link:disabled, .btn-link.disabled { + color: #6c757d; + pointer-events: none; +} + +.btn-lg { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.btn-sm { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 0.5rem; +} + +input[type=submit].btn-block, +input[type=reset].btn-block, +input[type=button].btn-block { + width: 100%; +} + +@keyframes spinner-border { + to { + transform: rotate(360deg); + } +} +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + animation: 0.75s linear infinite spinner-border; +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.2em; +} + +@keyframes spinner-grow { + 0% { + transform: scale(0); + } + 50% { + opacity: 1; + transform: none; + } +} +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + animation: 0.75s linear infinite spinner-grow; +} + +.spinner-grow-sm { + width: 1rem; + height: 1rem; +} + +@media (prefers-reduced-motion: reduce) { + .spinner-border, +.spinner-grow { + animation-duration: 1.5s; + } +} +.nav { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav-link { + display: block; + padding: 1rem 2rem; +} +.nav-link:hover, .nav-link:focus { + text-decoration: none; +} +.nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default; +} + +.nav-tabs { + border-bottom: 1px solid #dee2e6; +} +.nav-tabs .nav-link { + margin-bottom: -1px; + border: 1px solid transparent; + border-top-left-radius: 0.3rem; + border-top-right-radius: 0.3rem; +} +.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #dee2e6; +} +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; +} +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + border-radius: 0.3rem; +} +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; + background-color: #5942c1; +} + +.nav-fill > .nav-link, +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center; +} + +.nav-justified > .nav-link, +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center; +} + +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} + +.navbar { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem; +} +.navbar .container, +.navbar .container-fluid, +.navbar .container-sm, +.navbar .container-md, +.navbar .container-lg, +.navbar .container-xl { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; +} +.navbar-brand { + display: inline-block; + padding-top: 0.8125rem; + padding-bottom: 0.8125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; +} +.navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; +} + +.navbar-nav { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} +.navbar-nav .dropdown-menu { + position: static; + float: none; +} + +.navbar-text { + display: inline-block; + padding-top: 1rem; + padding-bottom: 1rem; +} + +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.3rem; +} +.navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: 50%/100% 100% no-repeat; +} + +.navbar-nav-scroll { + max-height: 75vh; + overflow-y: auto; +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, +.navbar-expand-sm > .container-fluid, +.navbar-expand-sm > .container-sm, +.navbar-expand-sm > .container-md, +.navbar-expand-sm > .container-lg, +.navbar-expand-sm > .container-xl { + padding-right: 0; + padding-left: 0; + } +} +@media (min-width: 576px) { + .navbar-expand-sm { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-sm > .container, +.navbar-expand-sm > .container-fluid, +.navbar-expand-sm > .container-sm, +.navbar-expand-sm > .container-md, +.navbar-expand-sm > .container-lg, +.navbar-expand-sm > .container-xl { + flex-wrap: nowrap; + } + .navbar-expand-sm .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-sm .navbar-toggler { + display: none; + } +} +@media (max-width: 767.98px) { + .navbar-expand-md > .container, +.navbar-expand-md > .container-fluid, +.navbar-expand-md > .container-sm, +.navbar-expand-md > .container-md, +.navbar-expand-md > .container-lg, +.navbar-expand-md > .container-xl { + padding-right: 0; + padding-left: 0; + } +} +@media (min-width: 768px) { + .navbar-expand-md { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-md > .container, +.navbar-expand-md > .container-fluid, +.navbar-expand-md > .container-sm, +.navbar-expand-md > .container-md, +.navbar-expand-md > .container-lg, +.navbar-expand-md > .container-xl { + flex-wrap: nowrap; + } + .navbar-expand-md .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-md .navbar-toggler { + display: none; + } +} +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, +.navbar-expand-lg > .container-fluid, +.navbar-expand-lg > .container-sm, +.navbar-expand-lg > .container-md, +.navbar-expand-lg > .container-lg, +.navbar-expand-lg > .container-xl { + padding-right: 0; + padding-left: 0; + } +} +@media (min-width: 992px) { + .navbar-expand-lg { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-lg > .container, +.navbar-expand-lg > .container-fluid, +.navbar-expand-lg > .container-sm, +.navbar-expand-lg > .container-md, +.navbar-expand-lg > .container-lg, +.navbar-expand-lg > .container-xl { + flex-wrap: nowrap; + } + .navbar-expand-lg .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-lg .navbar-toggler { + display: none; + } +} +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, +.navbar-expand-xl > .container-fluid, +.navbar-expand-xl > .container-sm, +.navbar-expand-xl > .container-md, +.navbar-expand-xl > .container-lg, +.navbar-expand-xl > .container-xl { + padding-right: 0; + padding-left: 0; + } +} +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-xl > .container, +.navbar-expand-xl > .container-fluid, +.navbar-expand-xl > .container-sm, +.navbar-expand-xl > .container-md, +.navbar-expand-xl > .container-lg, +.navbar-expand-xl > .container-xl { + flex-wrap: nowrap; + } + .navbar-expand-xl .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-xl .navbar-toggler { + display: none; + } +} +.navbar-expand { + flex-flow: row nowrap; + justify-content: flex-start; +} +.navbar-expand > .container, +.navbar-expand > .container-fluid, +.navbar-expand > .container-sm, +.navbar-expand > .container-md, +.navbar-expand > .container-lg, +.navbar-expand > .container-xl { + padding-right: 0; + padding-left: 0; +} +.navbar-expand .navbar-nav { + flex-direction: row; +} +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} +.navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; +} +.navbar-expand > .container, +.navbar-expand > .container-fluid, +.navbar-expand > .container-sm, +.navbar-expand > .container-md, +.navbar-expand > .container-lg, +.navbar-expand > .container-xl { + flex-wrap: nowrap; +} +.navbar-expand .navbar-nav-scroll { + overflow: visible; +} +.navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto; +} +.navbar-expand .navbar-toggler { + display: none; +} + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); +} +.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); +} +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); +} +.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); +} +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); +} +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); +} +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); +} +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); +} +.navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); +} +.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-dark .navbar-brand { + color: #fff; +} +.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; +} +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); +} +.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); +} +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; +} +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); +} +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); +} +.navbar-dark .navbar-text a { + color: #fff; +} +.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; +} + +.img-fluid { + max-width: 100%; + height: auto; +} + +.img-thumbnail { + padding: 0.25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: 0.3rem; + max-width: 100%; + height: auto; +} + +.figure { + display: inline-block; +} + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; +} + +.figure-caption { + font-size: 90%; + color: #6c757d; +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: "Exo 2", BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; +} +.tooltip.show { + opacity: 0.9; +} +.tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; +} +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^=top] { + padding: 0.4rem 0; +} +.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=top] .arrow { + bottom: 0; +} +.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=top] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^=right] { + padding: 0 0.4rem; +} +.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=right] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} +.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=right] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=bottom] { + padding: 0.4rem 0; +} +.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=bottom] .arrow { + top: 0; +} +.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=bottom] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^=left] { + padding: 0 0.4rem; +} +.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=left] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} +.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=left] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.3rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: "Exo 2", BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; +} +.popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; +} +.popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-popover-top, .bs-popover-auto[x-placement^=top] { + margin-bottom: 0.5rem; +} +.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=top] > .arrow { + bottom: calc(-0.5rem - 1px); +} +.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=top] > .arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); +} +.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=top] > .arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; +} + +.bs-popover-right, .bs-popover-auto[x-placement^=right] { + margin-left: 0.5rem; +} +.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=right] > .arrow { + left: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} +.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=right] > .arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); +} +.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=right] > .arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; +} + +.bs-popover-bottom, .bs-popover-auto[x-placement^=bottom] { + margin-top: 0.5rem; +} +.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=bottom] > .arrow { + top: calc(-0.5rem - 1px); +} +.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=bottom] > .arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); +} +.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=bottom] > .arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; +} +.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=bottom] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; +} + +.bs-popover-left, .bs-popover-auto[x-placement^=left] { + margin-right: 0.5rem; +} +.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=left] > .arrow { + right: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} +.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=left] > .arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); +} +.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=left] > .arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; +} + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 0.5rem 0.75rem; + color: #212529; +} + +html, +body { + min-height: 100%; + font-size: 16px; + text-rendering: geometricPrecision !important; +} + +#app { + display: flex; + flex-direction: column; + height: 100%; +} + +@media (min-width: 992px) { + .pt-lg-10 { + padding-top: 10rem !important; + } +} + +@media (min-width: 992px) { + .pb-lg-10 { + padding-bottom: 10rem !important; + } +} + +@media (min-width: 992px) { + .container-pricing-max-height { + max-height: 800px; + } +} + +h1, +.h1 { + font-family: "Exo 2", BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-weight: 800 !important; + text-transform: uppercase; + line-height: 1; + font-size: 2rem !important; +} +@media (min-width: 992px) { + h1, +.h1 { + font-size: 2.8rem !important; + } +} + +h3, +.h3 { + color: #fff; + font-weight: 700; + font-size: 1.5rem; +} + +.h4 { + color: #5942c1; + text-transform: uppercase; + font-weight: bold; + font-size: 1rem; + border-left: 3px solid #5942c1; + padding-left: 1rem; + margin-bottom: 1rem; +} + +.h5 { + display: inline-block; + font-size: 1.1rem; + padding: 0.65rem 1rem; + border-radius: 5px; + background-color: #ffe500; +} +.h5 + .lead { + font-size: 2.2rem; + font-weight: 700; + margin-bottom: 1rem; + line-height: 1.3; +} + +.font-size-l { + font-size: 3.2rem !important; +} + +.font-size-xl { + font-size: 4rem !important; +} + +.line-height-2 { + line-height: 2; +} + +.center-center { + display: flex; + align-items: center; + justify-content: center; +} + +.icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 50px; + height: 50px; + background: #343a40; +} +.icon i { + font-size: 1.2rem; +} +@media (min-width: 992px) { + .icon { + width: 80px; + height: 80px; + } + .icon i { + font-size: 2rem; + } +} + +.bg-before-dark { + position: relative; +} +.bg-before-dark:before { + content: ""; + background-color: #212529; + position: absolute; + width: 100%; + height: 325px; + top: -1px; + display: block; +} + +.bg-before-dark-contact { + position: relative; +} +.bg-before-dark-contact:before { + background-color: #212529; + position: absolute; + width: 100%; + left: 0; + bottom: 0; + height: 100px; + display: block; +} +@media (min-width: 992px) { + .bg-before-dark-contact:before { + content: ""; + } +} + +.btn-icon { + width: 50px !important; + height: 50px !important; + align-items: center; + justify-content: center; + display: inline-flex; + margin: 0 1rem; +} +.btn-icon i { + margin: 0 !important; + font-size: 1.3rem !important; +} + +.list-disc { + padding: 0; +} +.list-disc li { + margin-bottom: 0.5rem; +} +.list-disc li a { + color: #fff; +} +.list-disc li a:before { + content: ""; + display: inline-block; + width: 3px; + height: 3px; + background-color: #ffe500; + margin-right: 0.5rem; + margin-bottom: 0.2rem; +} + +.card-about-us .card-header { + height: 460px; + background-size: cover; +} + +.card-contact .card-header { + height: 200px; + border-top-right-radius: 10px; +} +@media (min-width: 992px) { + .card-contact .card-header { + height: 400px; + } +} + +.form-control-underlined, +.form-control-underlined:focus { + background: transparent; + box-shadow: none !important; + border: 0; + border-radius: 0; + border-bottom: 1px solid #9c9e9f; + color: #fff; + padding-left: 0; + padding-right: 0; +} + +.form-control-underlined:focus { + border-color: #ffe500; +} + +textarea.form-control-underlined { + resize: none; + height: 150px; +} + +.cursor-pointer { + cursor: pointer; +} +.cursor-pointer:hover { + cursor: pointer; +} + +[class*=btn-outline-] { + border-width: 3px; +} + +.btn { + font-family: "Exo 2", BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-weight: 400 !important; +} +.btn i { + font-size: 0.8rem; + margin-left: 0.5rem; +} + +.btn-chat { + position: fixed; + width: auto; + font-size: 1.2rem; + bottom: 1rem; + right: 1rem; +} + +.btn-nav { + display: block; + color: #000; + font-size: 18px; + font-weight: 700; + text-transform: uppercase; + margin: 0; + position: relative; +} +.btn-nav:focus { + box-shadow: none; +} +.btn-nav i { + margin-left: 0.5rem; + font-size: 0.875rem; +} + +.btn-sm { + font-size: 14px !important; +} + +@media (max-width: 768px) { + .btn-nav { + margin: auto; + color: black !important; + } + + .header-menu--right .btn-primary { + margin-top: 1rem; + } +} +.btn-remove { + height: 31px; + width: 31px; + background-color: transparent; + color: #ced4da; + border: 1px solid #ced4da; +} +.btn-remove:hover, .btn-remove:focus, .btn-remove:focus:hover { + background-color: transparent; + color: #dc3545; + border-color: #dc3545; +} +.btn-remove i { + margin: 0; +} + +.btn-tab { + flex: 1; + color: white; + background-color: #5942c1; + margin: 0 1rem; + background-size: contain; +} +.btn-tab img { + height: 20px; +} +@media (min-width: 768px) { + .btn-tab { + padding: 1.5rem 2rem; + } + .btn-tab img { + margin-bottom: 0.5rem; + height: 30px; + } +} +.btn-tab:not(.router-link-exact-active) { + background-image: none !important; + background-color: white; + color: #5942c1; +} +.btn-tab.router-link-exact-active:hover { + color: white; +} +.btn-tab:first-of-type { + margin-left: 0; +} +.btn-tab:last-of-type { + margin-right: 0; +} + +.btn-menu { + position: absolute; + z-index: 1; + color: white; + background: #212529; + border-radius: 0; + border-top-right-radius: 50%; + border-bottom-right-radius: 50%; + width: 40px; + height: 50px; + display: flex; + justify-content: center; + align-items: center; + left: calc(260px); + top: 50%; + margin-top: -25px; +} +.btn-menu.collapsed { + left: 0; + margin-left: 0; +} +.btn-menu:hover, .btn-menu:focus, .btn-menu:hover:focus { + color: white; + text-decoration: none; +} +.btn-menu i { + margin: 0; + font-size: 1.1rem; + margin-right: 10px; +} + +.btn-sign-in { + display: inline-flex; + align-items: center; + border: 1px solid #ced4da; + padding: 0; +} +.btn-sign-in .logo { + margin-right: 0.5rem; + background: white; + border-radius: 3px; + margin: 1px; + padding: 10px; + flex: 0 38px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 15px; +} +.btn-sign-in .logo img { + height: 22px; + width: 22px; +} +.btn-sign-in .label { + font-size: 16px; + color: white; + flex: 1; + text-align: left; +} +.btn-discord { + background-color: #7289da; +} +.btn-discord:active { + background-color: #7289da; +} +.btn-google { + background-color: #4285f4; +} +.btn-google:active { + background-color: #3367d6; +} +.btn-twitch { + background-color: #6441a5; +} +.btn-twitch:active { + background-color: #b9a3e3; +} +.btn-twitter { + background-color: #0098ff; +} +.btn-twitter:active { + background-color: #3367d6; +} + +.btn-spotify { + background-color: #1d75de; +} +.btn-spotify:active { + background-color: #1865c2; +} +.btn-github { + background-color: #171515; +} +.btn-github:active { + background-color: #171515; +} +.btn-metamask { + background-color: #037dd6; +} +.btn-metamask:active { + background-color: #037dd6; +} +.btn-shopify { + background-color: #96bf48; +} +.btn-shopify:active { + background-color: #3367d6; +} + +.card-logo { + display: flex; + border: 0; + flex: 0 0 120px; + background-color: #f8f9fa; + height: 120px; + width: 120px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 10px; +} +.card-logo:nth-child(3n) { + margin-right: 0; +} +.card-logo img { + margin: auto; + display: block; +} + +.card-logo-lg { + width: 200px; + height: 200px; + border-radius: 15px; +} + +a i.fa-question-circle { + color: #ced4da; +} + +body { + background-image: url("../img/bg.jpg") !important; + background-position: center center; + background-size: cover; + background-attachment: fixed; +} + +/*# sourceMappingURL=main.css.map */ diff --git a/apps/auth/src/assets/css/main.css.map b/apps/auth/src/assets/css/main.css.map new file mode 100644 index 000000000..42d1f4dbd --- /dev/null +++ b/apps/auth/src/assets/css/main.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../main.scss","../scss/_buttons.scss","../../node_modules/bootstrap/scss/_reboot.scss","../scss/_variables.scss","../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../node_modules/bootstrap/scss/mixins/_hover.scss","../../node_modules/bootstrap/scss/utilities/_align.scss","../../node_modules/bootstrap/scss/mixins/_background-variant.scss","../../node_modules/bootstrap/scss/utilities/_background.scss","../../node_modules/bootstrap/scss/utilities/_borders.scss","../../node_modules/bootstrap/scss/mixins/_clearfix.scss","../../node_modules/bootstrap/scss/utilities/_display.scss","../../node_modules/bootstrap/scss/mixins/_breakpoints.scss","../../node_modules/bootstrap/scss/utilities/_embed.scss","../../node_modules/bootstrap/scss/utilities/_flex.scss","../../node_modules/bootstrap/scss/utilities/_float.scss","../../node_modules/bootstrap/scss/utilities/_interactions.scss","../../node_modules/bootstrap/scss/utilities/_overflow.scss","../../node_modules/bootstrap/scss/utilities/_position.scss","../../node_modules/bootstrap/scss/utilities/_screenreaders.scss","../../node_modules/bootstrap/scss/mixins/_screen-reader.scss","../../node_modules/bootstrap/scss/utilities/_shadows.scss","../../node_modules/bootstrap/scss/utilities/_sizing.scss","../../node_modules/bootstrap/scss/utilities/_spacing.scss","../../node_modules/bootstrap/scss/utilities/_stretched-link.scss","../../node_modules/bootstrap/scss/utilities/_text.scss","../../node_modules/bootstrap/scss/mixins/_text-truncate.scss","../../node_modules/bootstrap/scss/mixins/_text-emphasis.scss","../../node_modules/bootstrap/scss/mixins/_text-hide.scss","../../node_modules/bootstrap/scss/utilities/_visibility.scss","../../node_modules/bootstrap/scss/_root.scss","../../node_modules/bootstrap/scss/_type.scss","../../node_modules/bootstrap/scss/mixins/_lists.scss","../../node_modules/bootstrap/scss/_card.scss","../../node_modules/bootstrap/scss/mixins/_border-radius.scss","../../node_modules/bootstrap/scss/_badge.scss","../../node_modules/bootstrap/scss/mixins/_transition.scss","../../node_modules/bootstrap/scss/mixins/_badge.scss","../../node_modules/bootstrap/scss/_variables.scss","../../node_modules/bootstrap/scss/_forms.scss","../../node_modules/bootstrap/scss/mixins/_forms.scss","../../node_modules/bootstrap/scss/mixins/_gradients.scss","../../node_modules/bootstrap/scss/_custom-forms.scss","../../node_modules/bootstrap/scss/_list-group.scss","../../node_modules/bootstrap/scss/mixins/_list-group.scss","../../node_modules/bootstrap/scss/_alert.scss","../../node_modules/bootstrap/scss/mixins/_alert.scss","../../node_modules/bootstrap/scss/_grid.scss","../../node_modules/bootstrap/scss/mixins/_grid.scss","../../node_modules/bootstrap/scss/mixins/_grid-framework.scss","../../node_modules/bootstrap/scss/_transitions.scss","../../node_modules/bootstrap/scss/_buttons.scss","../../node_modules/bootstrap/scss/mixins/_buttons.scss","../../node_modules/bootstrap/scss/_spinners.scss","../../node_modules/bootstrap/scss/_nav.scss","../../node_modules/bootstrap/scss/_navbar.scss","../../node_modules/bootstrap/scss/_images.scss","../../node_modules/bootstrap/scss/mixins/_image.scss","../../node_modules/bootstrap/scss/_tooltip.scss","../../node_modules/bootstrap/scss/mixins/_reset-text.scss","../../node_modules/bootstrap/scss/_popover.scss","../scss/_root.scss","../scss/_card.scss","../scss/_forms.scss"],"names":[],"mappings":";AAAQ;ACAA;ACkBR;AAAA;AAAA;EAGE;;;AAGF;EACE;EACA;EACA;EACA;;;AAMF;EACE;;;AAUF;EACE;EACA,aC2NuB;EC3InB,WAtCa;EFxCjB,aCqOmB;EDpOnB,aCyOiB;EDxOjB,OCnCS;EDoCT;EACA,kBC9CM;;;AD0DR;EACE;;;AASF;EACE;EACA;EACA;;;AAaF;EACE;EACA,eCuMuB;;;ADhMzB;EACE;EACA,eCsFwB;;;AD3E1B;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;;;AAGF;EACE,aCwIiB;;;ADrInB;EACE;EACA;;;AAGF;EACE;;;AAGF;AAAA;EAEE,aC2HmB;;;ADxHrB;EExFI;;;AFiGJ;AAAA;EAEE;EEnGE;EFqGF;EACA;;;AAGF;EAAM;;;AACN;EAAM;;;AAON;EACE,OCTW;EDUX,iBCTgB;EDUhB;;AGhLA;EHmLE,OCZe;EDaf,iBCZoB;;;ADqBxB;EACE;EACA;;AG/LA;EHkME;EACA;;;AASJ;AAAA;AAAA;AAAA;EAIE,aCgDsB;ECpMpB;;;AFwJJ;EAEE;EAEA;EAEA;EAGA;;;AAQF;EAEE;;;AAQF;EACE;EACA;;;AAGF;EAGE;EACA;;;AAQF;EACE;;;AAGF;EACE,aCmEmB;EDlEnB,gBCkEmB;EDjEnB,OCtQS;EDuQT;EACA;;;AAOF;EAEE;EACA;;;AAQF;EAEE;EACA,eC+IoB;;;ADzItB;EAEE;;;AAQF;EACE;;;AAGF;AAAA;AAAA;AAAA;AAAA;EAKE;EACA;EE5PE;EF8PF;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAMF;EACE;;;AAMF;EACE;;;AAOF;AAAA;AAAA;AAAA;EAIE;;;AASE;AAAA;AAAA;AAAA;EACE;;;AAMN;AAAA;AAAA;AAAA;EAIE;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAIF;EACE;EAEA;;;AAGF;EAME;EAEA;EACA;EACA;;;AAKF;EACE;EACA;EACA;EACA;EACA;EEzQM,WAhEW;EF2UjB;EACA;EACA;;AEvPM;EF8OR;IEtOY,WA9DM;;;;AFgTlB;EACE;;;AAIF;AAAA;EAEE;;;AAGF;EAKE;EACA;;;AAOF;EACE;;;AAQF;EACE;EACA;;;AAOF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAKF;EACE;;;AIheF;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;AACrB;EAAqB;;;ACFnB;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;AANJ;EACE;;;AFUF;AAAA;AAAA;EELI;;;ACCN;EACE;;;AAGF;EACE;;;ACXF;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAElB;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAmB;;;AAGjB;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AAIJ;EACE;;;AAOF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;ACxEA;EACE;EACA;EACA;;;ACOE;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;AAAxB;EAAwB;;;ACiD1B;EDjDE;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;ACiD1B;EDjDE;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;ACiD1B;EDjDE;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;ACiD1B;EDjDE;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;EAAxB;IAAwB;;;AAU9B;EAEI;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;EAArB;IAAqB;;;AErBzB;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;AAAA;AAAA;AAAA;AAAA;EAKE;EACA;EACA;EACA;EACA;EACA;EACA;;;AASA;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;ACzBF;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAEhC;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAC9B;EAA8B;;;AAE9B;EAAoC;;;AACpC;EAAoC;;;AACpC;EAAoC;;;AACpC;EAAoC;;;AACpC;EAAoC;;;AAEpC;EAAiC;;;AACjC;EAAiC;;;AACjC;EAAiC;;;AACjC;EAAiC;;;AACjC;EAAiC;;;AAEjC;EAAkC;;;AAClC;EAAkC;;;AAClC;EAAkC;;;AAClC;EAAkC;;;AAClC;EAAkC;;;AAClC;EAAkC;;;AAElC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AAChC;EAAgC;;;AFYhC;EElDA;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAEhC;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAE9B;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EAEpC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EAEjC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAElC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;AFYhC;EElDA;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAEhC;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAE9B;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EAEpC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EAEjC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAElC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;AFYhC;EElDA;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAEhC;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAE9B;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EAEpC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EAEjC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAElC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;AFYhC;EElDA;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAEhC;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAC9B;IAA8B;;;EAE9B;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EACpC;IAAoC;;;EAEpC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EACjC;IAAiC;;;EAEjC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAClC;IAAkC;;;EAElC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;EAChC;IAAgC;;;AC1ChC;EAAwB;;;AACxB;EAAwB;;;AACxB;EAAwB;;;AHoDxB;EGtDA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AHoDxB;EGtDA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AHoDxB;EGtDA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AHoDxB;EGtDA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;ACL1B;EAAyB;;;AAAzB;EAAyB;;;AAAzB;EAAyB;;;ACAzB;EAAsB;;;AAAtB;EAAsB;;;ACCtB;EAAyB;;;AAAzB;EAAyB;;;AAAzB;EAAyB;;;AAAzB;EAAyB;;;AAAzB;EAAyB;;;AAK3B;EACE;EACA;EACA;EACA;EACA,Sf2pBa;;;AexpBf;EACE;EACA;EACA;EACA;EACA,SfmpBa;;;Ae/oBb;EADF;IAEI;IACA;IACA,Sf2oBY;;;;AgBpqBhB;ECEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAUA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AC7BJ;EAAa;;;AACb;EAAU;;;AACV;EAAa;;;AACb;EAAe;;;ACCX;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAAvB;EAAuB;;;AAI3B;EAAU;;;AACV;EAAU;;;AAIV;EAAc;;;AACd;EAAc;;;AAEd;EAAU;;;AACV;EAAU;;;ACTF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAgC;;;AAChC;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAQF;EAAwB;;;AACxB;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAwB;;;AACxB;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAwB;;;AACxB;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAwB;;;AACxB;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAfF;EAAwB;;;AACxB;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAMN;EAAmB;;;AACnB;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AAEF;AAAA;EAEE;;;AXTF;EWlDI;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAQF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAMN;IAAmB;;;EACnB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;AXTF;EWlDI;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAQF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAMN;IAAmB;;;EACnB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;AXTF;EWlDI;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAQF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAMN;IAAmB;;;EACnB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;AXTF;EWlDI;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAgC;;;EAChC;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAQF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAfF;IAAwB;;;EACxB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAMN;IAAmB;;;EACnB;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;EAEF;AAAA;IAEE;;;AChEJ;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;;ACVJ;EAAkB;;;AAIlB;EAAiB;;;AACjB;EAAiB;;;AACjB;EAAiB;;;AACjB;ECTE;EACA;EACA;;;ADeE;EAAwB;;;AACxB;EAAwB;;;AACxB;EAAwB;;;AbqCxB;EavCA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AbqCxB;EavCA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AbqCxB;EavCA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AbqCxB;EavCA;IAAwB;;;EACxB;IAAwB;;;EACxB;IAAwB;;;AAM5B;EAAmB;;;AACnB;EAAmB;;;AACnB;EAAmB;;;AAInB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAuB;;;AACvB;EAAuB;;;AAIvB;EAAc;;;AEvCZ;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AANN;EACE;;;AtBUF;EsBLM;;;AFuCR;EAAa;;;AACb;EAAc;;;AAEd;EAAiB;;;AACjB;EAAiB;;;AAIjB;EGvDE;EACA;EACA;EACA;EACA;;;AHuDF;EAAwB;;;AAExB;EACE;EACA;;;AAKF;EAAc;;;AIjEd;EACE;;;AAGF;EACE;;;ACXF;EAGI;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAKF;EACA;;;ACXF;AAAA;EAEE,e5B4RuB;E4B1RvB,a5B4RqB;E4B3RrB,a5B4RqB;;;A4BxRvB;E3B0IQ,WAhEW;;AAsFX;E2BhKR;I3BwKY,WA9DM;;;;A2BzGlB;E3ByIQ,WAhEW;;AAsFX;E2B/JR;I3BuKY,WA9DM;;;;A2BxGlB;E3BwIQ,WAhEW;;AAsFX;E2B9JR;I3BsKY,WA9DM;;;;A2BvGlB;E3BuIQ,WAhEW;;AAsFX;E2B7JR;I3BqKY,WA9DM;;;;A2BtGlB;E3B4GM,WAtCa;;;A2BrEnB;E3B2GM,WAtCa;;;A2BnEnB;E3ByGM,WAtCa;E2BjEjB,a5B8PmB;;;A4B1PrB;E3B6HQ,WAhEW;E2B3DjB,a5BiRgB;E4BhRhB,a5BwQqB;;ACxHf;E2BnJR;I3B2JY,WA9DM;;;;A2BxFlB;E3BwHQ,WAhEW;E2BtDjB,a5B6QgB;E4B5QhB,a5BmQqB;;ACxHf;E2B9IR;I3BsJY,WA9DM;;;;A2BnFlB;E3BmHQ,WAhEW;E2BjDjB,a5ByQgB;E4BxQhB,a5B8PqB;;ACxHf;E2BzIR;I3BiJY,WA9DM;;;;A2B9ElB;E3B8GQ,WAhEW;E2B5CjB,a5BqQgB;E4BpQhB,a5ByPqB;;ACxHf;E2BpIR;I3B4IY,WA9DM;;;;A2BnElB;EACE,Y5B4EO;E4B3EP,e5B2EO;E4B1EP;EACA;;;AAQF;AAAA;E3BMI;E2BHF,a5BiNmB;;;A4B9MrB;AAAA;EAEE,S5ByPa;E4BxPb,kB5BiQQ;;;A4BzPV;EC/EE;EACA;;;ADmFF;ECpFE;EACA;;;ADsFF;EACE;;AAEA;EACE,c5B2OkB;;;A4BjOtB;E3BjCI;E2BmCF;;;AAIF;EACE,e5BmBO;ECJH,WAtCa;;;A2B2BnB;EACE;E3B7CE;E2B+CF,O5B1GS;;A4B4GT;EACE;;;AEtHJ;EACE;EACA;EACA;EACA;EAEA;EACA,kB9BJM;E8BKN;EACA;ECKE;;ADFF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;ECCF;EACA;;ADEA;EACE;ECUF;EACA;;ADJF;AAAA;EAEE;;;AAIJ;EAGE;EAGA;EACA,S9BowBc;;;A8BhwBhB;EACE,e9B8vBc;;;A8B3vBhB;EACE;EACA;;;AAGF;EACE;;;A5BrDA;E4B0DE;;AAGF;EACE,a9B6uBY;;;A8BruBhB;EACE;EACA;EAEA,kB9BsuBY;E8BruBZ;;AAEA;ECvEE;;;AD4EJ;EACE;EAEA,kB9B2tBY;E8B1tBZ;;AAEA;EClFE;;;AD4FJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA,S9BisByB;E+BhzBvB;;;ADmHJ;AAAA;AAAA;EAGE;EACA;;;AAGF;AAAA;ECjHI;EACA;;;ADqHJ;AAAA;ECxGI;EACA;;;ADgHF;EACE,e9ByqBgB;;ASxwBhB;EqB6FJ;IAMI;IACA;IACA;IACA;;EAEA;IAEE;IACA,c9B6pBc;I8B5pBd;IACA,a9B2pBc;;;;A8B9oBlB;EACE,e9B6oBgB;;ASxwBhB;EqBuHJ;IAQI;IACA;;EAGA;IAEE;IACA;;EAEA;IACE;IACA;;EAKA;ICzKJ;IACA;;ED2KM;AAAA;IAGE;;EAEF;AAAA;IAGE;;EAIJ;IC1KJ;IACA;;ED4KM;AAAA;IAGE;;EAEF;AAAA;IAGE;;;;AAcV;EACE,e9BkkBY;;AS1vBZ;EqBsLJ;IAMI,c9B+kBiB;I8B9kBjB,Y9B+kBe;I8B9kBf;IACA;;EAEA;IACE;IACA;;;;AAUN;EACE;;AAEA;EACE;;AAEA;EACE;ECvOF;EACA;;AD0OA;ECzPA;EACA;;AD4PA;ECtQA;EDwQE;;;AErRN;EACE;EACA;E/BiEE;E+B/DF,ahC8QiB;EgC7QjB;EACA;EACA;EACA;EDKE;EEFE,YDDJ;;ACKI;EDfN;ICgBQ;;;A/BLN;E8BGI;;;AAKJ;EACE;;;AAKJ;EACE;EACA;;;AAOF;EACE,ehCi3BqB;EgCh3BrB,chCg3BqB;E+Bv4BnB;;;ACgCF;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AFqCJ;EEjDA;EACA,kBC0Ea;;AjC5Db;EgCVI;EACA;;AAGF;EAEE;EACA;;;AEPN;EACE;EACA;EACA,QpC8da;EoC7db;EnCqHI,WAtCa;EmC5EjB,apCyQmB;EoCxQnB,apC6QiB;EoC5QjB,OpCDS;EoCET,kBpCTM;EoCUN;EACA;ELAE;EEFE,YGQJ;;AHJI;EGdN;IHeQ;;;AGMN;EACE;EACA;;AAIF;EACE;EACA;;ACtBF;EACE;EACA,kBrCRI;EqCSJ,crCycuB;EqCxcvB;EAKE,YrCyWuB;;AoCrV3B;EACE,OpC9BO;EoCgCP;;AAQF;EAEE,kBpC9CO;EoCgDP;;;AAQF;AAAA;AAAA;AAAA;EACE;;;AAKF;EAME,OpC/DO;EoCgEP,kBpCvEI;;;AoC4ER;AAAA;EAEE;EACA;;;AAUF;EACE;EACA;EACA;EnC3BE;EmC6BF,apCsLiB;;;AoCnLnB;EACE;EACA;EnCqBI,WAtCa;EmCmBjB,apC2He;;;AoCxHjB;EACE;EACA;EnCcI,WAtCa;EmC0BjB,apCqHe;;;AoC5GjB;EACE;EACA;EACA;EACA;EnCDI,WAtCa;EmCyCjB,apCyJiB;EoCxJjB,OpCnHS;EoCoHT;EACA;EACA;;AAEA;EAEE;EACA;;;AAYJ;EACE,QpCgVgB;EoC/UhB;EnC1BI,WAtCa;EmCkEjB,apC6Ee;E+BtNb;;;AK6IJ;EACE,QpCyUgB;EoCxUhB;EnClCI,WAtCa;EmC0EjB,apCoEe;E+BrNb;;;AKuJF;EAEE;;;AAIJ;EACE;;;AAQF;EACE,epC8TyB;;;AoC3T3B;EACE;EACA,YpC+SqB;;;AoCvSvB;EACE;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;;;AASJ;EACE;EACA;EACA,cpCoRwB;;;AoCjR1B;EACE;EACA,YpCgR0B;EoC/Q1B;;AAGA;EAEE,OpCzNO;;;AoC6NX;EACE;;;AAGF;EACE;EACA;EACA;EACA,cpCiQ2B;;AoC9P3B;EACE;EACA;EACA,cpC4P+B;EoC3P/B;;;AC7MF;EACE;EACA;EACA,YrC8bmB;ECranB;EoCvBA,ODqNqC;;;AClNvC;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EpCmEE,WAtCa;EoC3Bf,arC6Ne;EqC5Nf;EACA;EN9CA;;AMmDA;EAEE;;;AAKF;AAAA;AAAA;AAAA;EAEE;;;AA9CF;EAoDE,cDkLmC;EC/KjC,erC4Ya;EqC3Yb;EACA;EACA;EACA;;AAGF;EACE,cDuKiC;ECtKjC;;;AAhEJ;EAyEI,erC0Xa;EqCzXb;;;AA1EJ;EAiFE,cDqJmC;EClJjC,erCgdoC;EqC/cpC;;AAGF;EACE,cD6IiC;EC5IjC;;;AAOF;EACE,ODoIiC;;ACjInC;AAAA;AAAA;EAEE;;;AAOF;EACE,ODuHiC;;ACrHjC;EACE,cDoH+B;;AC/GjC;EACE;EClJN,kBDmJ2B;;AAKvB;EACE;;AAGF;EACE,cAVqB;;;AAmBzB;EACE,cApBuB;;AAwBvB;EACE,cAzBqB;EA0BrB;;;AAvIR;EACE;EACA;EACA,YrC8bmB;ECranB;EoCvBA,ODqNqC;;;AClNvC;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EpCmEE,WAtCa;EoC3Bf,arC6Ne;EqC5Nf;EACA;EN9CA;;AMmDA;EAEE;;;AAKF;AAAA;AAAA;AAAA;EAEE;;;AA9CF;EAoDE,cDkLmC;EC/KjC,erC4Ya;EqC3Yb;EACA;EACA;EACA;;AAGF;EACE,cDuKiC;ECtKjC;;;AAhEJ;EAyEI,erC0Xa;EqCzXb;;;AA1EJ;EAiFE,cDqJmC;EClJjC,erCgdoC;EqC/cpC;;AAGF;EACE,cD6IiC;EC5IjC;;;AAOF;EACE,ODoIiC;;ACjInC;AAAA;AAAA;EAEE;;;AAOF;EACE,ODuHiC;;ACrHjC;EACE,cDoH+B;;AC/GjC;EACE;EClJN,kBDmJ2B;;AAKvB;EACE;;AAGF;EACE,cAVqB;;;AAmBzB;EACE,cApBuB;;AAwBvB;EACE,cAzBqB;EA0BrB;;;AD+FV;EACE;EACA;EACA;;AAKA;EACE;;A3B/NA;E2BoOA;IACE;IACA;IACA;IACA;;EAIF;IACE;IACA;IACA;IACA;IACA;;EAIF;IACE;IACA;IACA;;EAIF;IACE;;EAGF;AAAA;IAEE;;EAKF;IACE;IACA;IACA;IACA;IACA;;EAEF;IACE;IACA;IACA;IACA,cpCmKsB;IoClKtB;;EAGF;IACE;IACA;;EAEF;IACE;;;;AG9UN;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA,cvC6ewB;;;AuC1e1B;EACE;EACA;EACA;EACA,OvCye8B;EuCxe9B;EACA;;AAEA;EACE,OvCzBI;EuC0BJ,cvCuNkB;EsClPlB,kBtCkPkB;;AuClNpB;EAKI,YvCoVuB;;AuChV3B;EACE,cvCyauB;;AuCtazB;EACE,OvC7CI;EuC8CJ,kBvCseiC;EuCrejC,cvCqeiC;;AuC9djC;EACE,OvCjDK;;AuCmDL;EACE,kBvCxDG;;;AuCkEX;EACE;EACA;EAEA;;AAIA;EACE;EACA;EACA;EACA;EACA,OvC4a4B;EuC3a5B,QvC2a4B;EuC1a5B;EACA;EACA,kBvCrFI;EuCsFJ;;AAKF;EACE;EACA;EACA;EACA;EACA,OvC6Z4B;EuC5Z5B,QvC4Z4B;EuC3Z5B;EACA;;;AAUF;ERlGE;;AQuGA;EACE;;AAKF;EACE,cvCwHgB;EsClPlB,kBtCkPkB;;AuCpHlB;EACE;;AAKF;EDpIA,kBtC6gB2C;;AuCtY3C;EDvIA,kBtC6gB2C;;;AuC3X7C;EAEE,evC8YmC;;AuC1YnC;EACE;;AAKF;ED9JA,kBtC6gB2C;;;AuCpW/C;EACE;;AAGE;EACE;EACA,OvCsXgB;EuCrXhB;EAEA,evCoXkC;;AuCjXpC;EACE;EACA;EACA,OvC+WyB;EuC9WzB,QvC8WyB;EuC7WzB,kBvCpLK;EuCsLL,evC0WkC;EiC5hBlC,YMmLA;;AN/KA;EMuKF;INtKI;;;AMmLJ;EACE,kBvClME;EuCmMF;;AAKF;EDzMA,kBtC6gB2C;;;AuCvT/C;EACE;EACA;EACA,QvCwQa;EuCvQb;EtCjGI,WAtCa;EsC0IjB,avCmDmB;EuClDnB,avCuDiB;EuCtDjB,OvCvNS;EuCwNT;EACA;EACA;ERtNE;EQyNF;;AAEA;EACE,cvC2OuB;EuC1OvB;EAKE,YvC8V2B;;AuC3V7B;EAME,OvC/OK;EuCgPL,kBvCvPE;;AuC2PN;EAEE;EACA,evCmHkB;EuClHlB;;AAGF;EACE,OvC7PO;EuC8PP,kBvClQO;;AuCsQT;EACE;;AAIF;EACE;EACA;;;AAIJ;EACE,QvC6MgB;EuC5MhB,avCqGuB;EuCpGvB,gBvCoGuB;EuCnGvB,cvCoGuB;ECnQnB,WAtCa;;;AsCyMnB;EACE,QvCsMgB;EuCrMhB,avCkGuB;EuCjGvB,gBvCiGuB;EuChGvB,cvCiGuB;ECxQnB,WAtCa;;;AsCsNnB;EACE;EACA;EACA;EACA,QvCoLa;EuCnLb;;;AAGF;EACE;EACA;EACA;EACA,QvC4Ka;EuC3Kb;EACA;EACA;;AAEA;EACE,cvCwJuB;EuCvJvB,YvC6DyB;;AuCzD3B;EAEE,kBvC/TO;;AuCmUP;EACE,SvCsTa;;AuClTjB;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA,QvC2Ia;EuC1Ib;EACA;EAEA,avC1EmB;EuC2EnB,avCtEiB;EuCuEjB,OvCpVS;EuCqVT,kBvC5VM;EuC6VN;ERlVE;;AQsVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,QvCoHiB;EuCnHjB;EACA,avCtFe;EuCuFf,OvCpWO;EuCqWP;ED7WA,kBtCGO;EuC4WP;ERnWA;;;AQ8WJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIA;EAA0B,YvC+NQ;;AuC9NlC;EAA0B,YvC8NQ;;AuC7NlC;EAA0B,YvC6NQ;;AuC1NpC;EACE;;AAGF;EACE,OvC+MuB;EuC9MvB,QvC8MuB;EuC7MvB;EDlZA,kBtCkPkB;EuCkKlB,QvC8MwB;E+BtlBxB;EEFE,YM6YF;EACA;;AN1YE;EMiYJ;INhYM;;;AM2YJ;ED1ZA,kBtCumB2B;;AuCxM7B;EACE,OvCwLuB;EuCvLvB,QvCwLwB;EuCvLxB;EACA,QvCuLwB;EuCtLxB,kBvChaO;EuCiaP;ERzZA;;AQ8ZF;EACE,OvCoLuB;EuCnLvB,QvCmLuB;EsC/lBvB,kBtCkPkB;EuC4LlB,QvCoLwB;E+BtlBxB;EEFE,YMuaF;EACA;;ANpaE;EM4ZJ;IN3ZM;;;AMqaJ;EDpbA,kBtCumB2B;;AuC9K7B;EACE,OvC8JuB;EuC7JvB,QvC8JwB;EuC7JxB;EACA,QvC6JwB;EuC5JxB,kBvC1bO;EuC2bP;ERnbA;;AQwbF;EACE,OvC0JuB;EuCzJvB,QvCyJuB;EuCxJvB;EACA,cvCjFoB;EuCkFpB,avClFoB;EsCvXpB,kBtCkPkB;EuCyNlB,QvCuJwB;E+BtlBxB;EEFE,YMocF;EACA;;ANjcE;EMsbJ;INrbM;;;AMkcJ;EDjdA,kBtCumB2B;;AuCjJ7B;EACE,OvCiIuB;EuChIvB,QvCiIwB;EuChIxB;EACA,QvCgIwB;EuC/HxB;EACA;EACA;;AAIF;EACE,kBvC9dO;E+BQP;;AQ0dF;EACE;EACA,kBvCpeO;E+BQP;;AQieA;EACE,kBvCxeK;;AuC2eP;EACE;;AAGF;EACE,kBvChfK;;AuCmfP;EACE;;AAGF;EACE,kBvCxfK;;;AuC6fX;AAAA;AAAA;ENzfM,YM4fJ;;ANxfI;EMqfN;AAAA;AAAA;INpfQ;;;;AOjBR;EACE;EACA;EAGA;EACA;ETQE;;;ASEJ;EACE;EACA,OxCRS;EwCST;;AtCPA;EsCWE;EACA,OxCdO;EwCeP;EACA,kBxCtBO;;AwCyBT;EACE,OxClBO;EwCmBP,kBxC1BO;;;AwCmCX;EACE;EACA;EACA;EAGA,kBxC3CM;EwC4CN;;AAEA;ET1BE;EACA;;AS6BF;EThBE;EACA;;ASmBF;EAEE,OxClDO;EwCmDP;EACA,kBxC1DI;;AwC8DN;EACE;EACA,OxChEI;EwCiEJ,kBxCgLkB;EwC/KlB,cxC+KkB;;AwC5KpB;EACE;;AAEA;EACE;EACA,kBxCyJS;;;AwC3IX;EACE;;AAGE;ET1BJ;EAZA;;AS2CI;ET3CJ;EAYA;;ASoCI;EACE;;AAGF;EACE,kBxCwHK;EwCvHL;;AAEA;EACE;EACA,mBxCmHG;;;AS9KX;E+BmCA;IACE;;EAGE;IT1BJ;IAZA;;ES2CI;IT3CJ;IAYA;;ESoCI;IACE;;EAGF;IACE,kBxCwHK;IwCvHL;;EAEA;IACE;IACA,mBxCmHG;;;AS9KX;E+BmCA;IACE;;EAGE;IT1BJ;IAZA;;ES2CI;IT3CJ;IAYA;;ESoCI;IACE;;EAGF;IACE,kBxCwHK;IwCvHL;;EAEA;IACE;IACA,mBxCmHG;;;AS9KX;E+BmCA;IACE;;EAGE;IT1BJ;IAZA;;ES2CI;IT3CJ;IAYA;;ESoCI;IACE;;EAGF;IACE,kBxCwHK;IwCvHL;;EAEA;IACE;IACA,mBxCmHG;;;AS9KX;E+BmCA;IACE;;EAGE;IT1BJ;IAZA;;ES2CI;IT3CJ;IAYA;;ESoCI;IACE;;EAGF;IACE,kBxCwHK;IwCvHL;;EAEA;IACE;IACA,mBxCmHG;;;AwCrGf;ETnHI;;ASsHF;EACE;;AAEA;EACE;;;ACzIJ;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;ACrJxE;EACE,ODoJsE;ECnJtE,kBDmJuC;;AtCxIzC;EuCPM,OD+IkE;EC9IlE;;AAGF;EACE,OzCPA;EyCQA,kBDyIkE;ECxIlE,cDwIkE;;;AEpJ1E;EACE;EACA;EACA,e1Cu8BoB;E0Ct8BpB;EXUE;;;AWLJ;EAEE;;;AAIF;EACE,a1CmQiB;;;A0C3PnB;EACE;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAUF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ADsCF;EC/CA,ODgDqH;EJ3CnH,kBI2CuB;EC9CzB,cD8CqE;;AC5CrE;EACE;;AAGF;EACE;;;ACJF;AAAA;AAAA;AAAA;AAAA;AAAA;ECDA;EACA;EACA;EACA;EACA;;;ApCmDE;EmCzCE;IACE,W5C8Le;;;AStJnB;EmCzCE;IACE,W5C8Le;;;AStJnB;EmCzCE;IACE,W5C8Le;;;AStJnB;EmCzCE;IACE,W5C8Le;;;A4ClKrB;ECnCA;EACA;EACA;EACA;;;ADsCA;EACE;EACA;;AAEA;AAAA;EAEE;EACA;;;AEtDJ;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;;;AAsBE;EACE;EACA;EACA;;;AD4BN;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;ACnBE;EDCJ;EACA;EACA;;;ACGQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACSQ;EDbR;EAIA;;;ACeI;EAAwB;;;AAExB;EAAuB;;;AAGrB;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AACX;EAAwB,OADb;;;AAQP;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ACgBU;EDhBV;;;ApCKE;EqC3BE;IACE;IACA;IACA;;;ED4BN;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;ECnBE;IDCJ;IACA;IACA;;;ECGQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECeI;IAAwB;;;EAExB;IAAuB;;;EAGrB;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EAQP;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ApCKE;EqC3BE;IACE;IACA;IACA;;;ED4BN;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;ECnBE;IDCJ;IACA;IACA;;;ECGQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECeI;IAAwB;;;EAExB;IAAuB;;;EAGrB;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EAQP;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ApCKE;EqC3BE;IACE;IACA;IACA;;;ED4BN;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;ECnBE;IDCJ;IACA;IACA;;;ECGQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECeI;IAAwB;;;EAExB;IAAuB;;;EAGrB;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EAQP;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ApCKE;EqC3BE;IACE;IACA;IACA;;;ED4BN;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;EAFF;IACE;IACA;;;ECnBE;IDCJ;IACA;IACA;;;ECGQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECSQ;IDbR;IAIA;;;ECeI;IAAwB;;;EAExB;IAAuB;;;EAGrB;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EACX;IAAwB,OADb;;;EAQP;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;ECgBU;IDhBV;;;AEvDF;EdgBM,YcfJ;;AdmBI;EcpBN;IdqBQ;;;AclBN;EACE;;;AAKF;EACE;;;AAIJ;EACE;EACA;EACA;EdDI,YcEJ;;AdEI;EcNN;IdOQ;;;;AefR;EACE;EAEA,ahD6QmB;EgD5QnB,OhDMS;EgDLT;EAGA;EACA;EACA;EACA;ECuFA;EhDuBI,WAtCa;EgDiBjB,ajDiLiB;E+BzQf;EEFE,YeGJ;;AfCI;EedN;IfeQ;;;A/BTN;E8CUE,OhDNO;EgDOP;;AAGF;EAEE;EACA,YhDkWyB;;AgD9V3B;EAEE,ShDqYmB;;AgDjYrB;EACE;;AAcJ;AAAA;EAEE;;;AASA;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADQN;EC3DA;EXAE,kBHsEW;EcpEb,cdoEa;;AjChEb;E+CAE;EXNA,kBWD2D;EAS3D,cATqG;;AAYvG;EAEE;EXbA,kBWD2D;EAgB3D,cAhBqG;EAqBnG;;AAKJ;EAEE;EACA,kBd0CW;EczCX,cdyCW;;AclCb;EAGE;EACA,kBAzC+I;EA6C/I,cA7CyL;;AA+CzL;EAKI;;;ADcN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADzBN;ECPA,OdYa;EcXb,cdWa;;AjChEb;E+CwDE,OALgD;EAMhD,kBdOW;EcNX,cdMW;;AcHb;EAEE;;AAGF;EAEE,OdJW;EcKX;;AAGF;EAGE;EACA,kBdZW;EcaX,cdbW;;AceX;EAKI;;;ADdR;EACE,ahDmMmB;EgDlMnB,OhD6FW;EgD5FX,iBhD6FgB;;AEtKhB;E8C4EE,OhD2Fe;EgD1Ff,iBhD2FoB;;AgDxFtB;EAEE,iBhDsFoB;;AgDnFtB;EAEE,OhDtFO;EgDuFP;;;AAWJ;ECPE;EhDuBI,WAtCa;EgDiBjB,ajD6He;E+BrNb;;;AiBiGJ;ECXE;EhDuBI,WAtCa;EgDiBjB,ajD8He;E+BtNb;;;AiB0GJ;EACE;EACA;;AAGA;EACE,YhD2SkB;;;AgDnSpB;AAAA;AAAA;EACE;;;AEvIJ;EACE;IAAK;;;AAGP;EACE;EACA,OlD6iCc;EkD5iCd,QlD4iCc;EkD3iCd;EACA;EACA;EAEA;EACA;;;AAGF;EACE,OlDsiCiB;EkDriCjB,QlDqiCiB;EkDpiCjB,clDsiCwB;;;AkD/hC1B;EACE;IACE;;EAEF;IACE;IACA;;;AAIJ;EACE;EACA,OlD6gCc;EkD5gCd,QlD4gCc;EkD3gCd;EACA;EAEA;EACA;EACA;;;AAGF;EACE,OlDsgCiB;EkDrgCjB,QlDqgCiB;;;AkDjgCjB;EACE;AAAA;IAEE;;;ACxDN;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;AjDCA;EiDGE;;AAIF;EACE,OnDXO;EmDYP;EACA;;;AAQJ;EACE;;AAEA;EACE;EACA;EpBZA;EACA;;A7BZF;EiD2BI,cnD6oB6B;;AmD1oB/B;EACE,OnDlCK;EmDmCL;EACA;;AAIJ;AAAA;EAEE,OnDzCO;EmD0CP,kBnDjDI;EmDkDJ,cnDkoBgC;;AmD/nBlC;EAEE;EpBnCA;EACA;;;AoB8CF;EpBxDE;;AoB4DF;AAAA;EAEE,OnDzEI;EmD0EJ,kBnDuKkB;;;AmD7JpB;AAAA;EAEE;EACA;;;AAKF;AAAA;EAEE;EACA;EACA;;;AAUF;EACE;;AAEF;EACE;;;ACpGJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAIA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;;AAoBJ;EACE;EACA,apD0pBuB;EoDzpBvB,gBpDypBuB;EoDxpBvB,cpD4EO;ECJH,WAtCa;EmDhCjB;EACA;;AlD1CA;EkD6CE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AASJ;EACE;EACA,apDklBmB;EoDjlBnB,gBpDilBmB;;;AoDrkBrB;EACE;EACA;EAGA;;;AAIF;EACE;EnDSI,WAtCa;EmD+BjB;EACA;EACA;ErBxGE;;A7BFF;EkD8GE;;;AAMJ;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE,YpDwkB6B;EoDvkB7B;;;A3CtEE;E2CkFI;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;IACA;;;A3CjGN;E2C6FA;IAoBI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE,epDihBgB;IoDhhBhB,cpDghBgB;;EoD3gBpB;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;;EAcF;IACE;;EAGF;IACE;IAGA;;EAGF;IACE;;;A3ChJN;E2CkFI;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;IACA;;;A3CjGN;E2C6FA;IAoBI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE,epDihBgB;IoDhhBhB,cpDghBgB;;EoD3gBpB;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;;EAcF;IACE;;EAGF;IACE;IAGA;;EAGF;IACE;;;A3ChJN;E2CkFI;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;IACA;;;A3CjGN;E2C6FA;IAoBI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE,epDihBgB;IoDhhBhB,cpDghBgB;;EoD3gBpB;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;;EAcF;IACE;;EAGF;IACE;IAGA;;EAGF;IACE;;;A3ChJN;E2CkFI;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;IACA;;;A3CjGN;E2C6FA;IAoBI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE,epDihBgB;IoDhhBhB,cpDghBgB;;EoD3gBpB;AAAA;AAAA;AAAA;AAAA;AAAA;IACE;;EAcF;IACE;;EAGF;IACE;IAGA;;EAGF;IACE;;;AAhEN;EAoBI;EACA;;AAnBA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAmBF;EACE;;AAEA;EACE;;AAGF;EACE,epDihBgB;EoDhhBhB,cpDghBgB;;AoD3gBpB;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAcF;EACE;;AAGF;EACE;EAGA;;AAGF;EACE;;;AAcR;EACE,OpDyfwB;;AEjtB1B;EkD2NI,OpDsfsB;;AoDjfxB;EACE,OpD8ee;;AE/sBnB;EkDoOM,OpD4emB;;AoDzerB;EACE,OpD0esB;;AoDte1B;AAAA;AAAA;AAAA;EAIE,OpDiesB;;AoD7d1B;EACE,OpD0diB;EoDzdjB,cpD8dgC;;AoD3dlC;EACE;;AAGF;EACE,OpDidiB;;AoDhdjB;EACE,OpDidsB;;AEjtB1B;EkDmQM,OpD8coB;;;AoDtc1B;EACE,OpDrRI;;AESN;EkD+QI,OpDxRE;;AoD6RJ;EACE,OpDmbc;;AExsBlB;EkDwRM,OpDibkB;;AoD9apB;EACE,OpD+aqB;;AoD3azB;AAAA;AAAA;AAAA;EAIE,OpD7SE;;AoDiTN;EACE,OpD+ZgB;EoD9ZhB,cpDma+B;;AoDhajC;EACE;;AAGF;EACE,OpDsZgB;;AoDrZhB;EACE,OpD7TE;;AESN;EkDuTM,OpDhUA;;;AqDAR;ECIE;EAGA;;;ADDF;EACE,SrDg/BkB;EqD/+BlB,kBrDRM;EqDSN;EtBEE;EuBPF;EAGA;;;ADcF;EAEE;;;AAGF;EACE;EACA;;;AAGF;EpDkCI;EoDhCF,OrD3BS;;;AuDZX;EACE;EACA,SvD0qBe;EuDzqBf;EACA,QvDg1Be;EwDp1Bf,axDyQuB;EwDvQvB;EACA,axDkRmB;EwDjRnB,axDsRiB;EwDrRjB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EvDgHI,WAtCa;EsD9EjB;EACA;;AAEA;EAAS,SvDo0BO;;AuDl0BhB;EACE;EACA;EACA,OvDo0BkB;EuDn0BlB,QvDo0BmB;;AuDl0BnB;EACE;EACA;EACA;EACA;;;AAKN;EACE;;AAEA;EACE;;AAEA;EACE;EACA;EACA,kBvDvBE;;;AuD4BR;EACE;;AAEA;EACE;EACA,OvDsyBmB;EuDryBnB,QvDoyBkB;;AuDlyBlB;EACE;EACA;EACA,oBvDvCE;;;AuD4CR;EACE;;AAEA;EACE;;AAEA;EACE;EACA;EACA,qBvDrDE;;;AuD0DR;EACE;;AAEA;EACE;EACA,OvDwwBmB;EuDvwBnB,QvDswBkB;;AuDpwBlB;EACE;EACA;EACA,mBvDrEE;;;AuD0FR;EACE,WvDkuBkB;EuDjuBlB;EACA,OvDvGM;EuDwGN;EACA,kBvD/FM;E+BCJ;;;A0BlBJ;EACE;EACA;EACA;EACA,SzDwqBe;EyDvqBf;EACA,WzDi2BkB;EwDt2BlB,axDyQuB;EwDvQvB;EACA,axDkRmB;EwDjRnB,axDsRiB;EwDrRjB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EvDgHI,WAtCa;EwD7EjB;EACA,kBzDNM;EyDON;EACA;E1BGE;;A0BCF;EACE;EACA;EACA,OzDi2BkB;EyDh2BlB,QzDi2BmB;EyDh2BnB;;AAEA;EAEE;EACA;EACA;EACA;EACA;;;AAKN;EACE,ezDk1BqB;;AyDh1BrB;EACE;;AAEA;EACE;EACA;EACA,kBzD60BsB;;AyD10BxB;EACE,QzDwLS;EyDvLT;EACA,kBzD7CE;;;AyDkDR;EACE,azD8zBqB;;AyD5zBrB;EACE;EACA,OzD0zBmB;EyDzzBnB,QzDwzBkB;EyDvzBlB;;AAEA;EACE;EACA;EACA,oBzDszBsB;;AyDnzBxB;EACE,MzDiKS;EyDhKT;EACA,oBzDpEE;;;AyDyER;EACE,YzDuyBqB;;AyDryBrB;EACE;;AAEA;EACE;EACA;EACA,qBzDkyBsB;;AyD/xBxB;EACE,KzD6IS;EyD5IT;EACA,qBzDxFE;;AyD6FN;EACE;EACA;EACA;EACA;EACA,OzD8wBkB;EyD7wBlB;EACA;EACA;;;AAIJ;EACE,czDuwBqB;;AyDrwBrB;EACE;EACA,OzDmwBmB;EyDlwBnB,QzDiwBkB;EyDhwBlB;;AAEA;EACE;EACA;EACA,mBzD+vBsB;;AyD5vBxB;EACE,OzD0GS;EyDzGT;EACA,mBzD3HE;;;AyDiJR;EACE;EACA;ExD3BI,WAtCa;EwDoEjB,kBzDitBkB;EyDhtBlB;E1BnIE;EACA;;A0BqIF;EACE;;;AAIJ;EACE;EACA,OzDxJS;;;A0DhBX;AAAA;EAEI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAIA;EADJ;IAEQ;;;;AAKJ;EADJ;IAEQ;;;;AAKJ;EADJ;IAEQ;;;;AAIR;AAAA;EAEI,a1DyOqB;E0DxOrB;EACA;EACA;EACA;;AAEA;EARJ;AAAA;IASQ;;;;AAIR;AAAA;EAEI,O1DvCI;E0DwCJ,a1DwOe;E0DvOf;;;AAGJ;EACI,O1DfK;E0DgBL;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA,kB1DzBK;;A0D2BL;EACI;EACA,a1DiNW;E0DhNX;EACA;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,Y1DzBG;;A0D2BH;EACI;;AAGJ;EAZJ;IAaQ;IACA;;EAEA;IACI;;;;AAKZ;EACI;;AAEA;EACI;EACA,kB1D7CC;E0D8CD;EACA;EACA;EACA;EACA;;;AAIR;EACI;;AAEA;EACI,kB1D1DC;E0D2DD;EACA;EACA;EACA;EACA;EACA;;AAEA;EATJ;IAUQ;;;;AAIZ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;;AAIR;EACI;;AAEA;EACI;;AAEA;EACI,O1DjKJ;;A0DmKI;EACI;EACA;EACA;EACA;EACA,kB1DtIP;E0DuIO;EACA;;;AAOZ;EACI;EACA;;;AAKJ;EACI;EACA;;AAEA;EAJJ;IAKQ;;;;AAKZ;AAAA;EAEI;EACA;EACA;EACA;EACA;EACA,O1DzMI;E0D0MJ;EACA;;;AAEJ;EACI,c1D5KK;;;A0D+KT;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;;;A5D/NR;EACI;;;AAGJ;EACI,aEmQqB;EFlQrB;;AAEA;EACI;EACA;;;AAIR;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA,WE2OO;;;AFvOf;EACI;;;AAGJ;EACI;IACI;IACA;;;EAEJ;IACI;;;AAIR;EACI;EACA;EACA;EACA,OElDO;EFmDP;;AAEA;EAGI;EACA,OE7BF;EF8BE,cE9BF;;AFiCF;EACI;;;AAIR;EACI;EACA;EACA,kBE3CK;EF4CL;EACA;;AAEA;EACI;;AAGJ;EAXJ;IAYQ;;EAEA;IACI;IACA;;;AAIR;EACI;EACA;EACA,OE/DC;;AFkEL;EACI;;AAGJ;EACI;;AAEJ;EACI;;;AAIR;EACI;EACA;EACA;EACA,YE3CK;EF4CL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EAGI;EACA;;AAGJ;EACI;EACA;EACA;;;AAIR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;;AAEA;EACI;;;AAIR;EACI;;AAEA;EACI;;;AAIR;EACI;;AAEA;EACI;;;AAIR;EACI;;AAEA;EACI;;;A6DpNR;EACI;EACA;EACA;EACA,kB3DIO;E2DHP;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;;AAIR;EACI;EACA;EACA;;;ACzBJ;EACI,O5DUO;;;AH4BX;EACI;EACA;EACA","file":"main.css"} \ No newline at end of file diff --git a/apps/auth/src/assets/img/bg.jpg b/apps/auth/src/assets/img/bg.jpg new file mode 100644 index 000000000..a72edc556 Binary files /dev/null and b/apps/auth/src/assets/img/bg.jpg differ diff --git a/apps/auth/src/assets/img/discord-logo.png b/apps/auth/src/assets/img/discord-logo.png new file mode 100644 index 000000000..fb6a4b4ba Binary files /dev/null and b/apps/auth/src/assets/img/discord-logo.png differ diff --git a/apps/auth/src/assets/img/g-logo.png b/apps/auth/src/assets/img/g-logo.png new file mode 100644 index 000000000..3a2e0c537 Binary files /dev/null and b/apps/auth/src/assets/img/g-logo.png differ diff --git a/apps/auth/src/assets/img/github-logo.png b/apps/auth/src/assets/img/github-logo.png new file mode 100644 index 000000000..56b2531f1 Binary files /dev/null and b/apps/auth/src/assets/img/github-logo.png differ diff --git a/apps/auth/src/assets/img/icons/android-chrome-192x192.png b/apps/auth/src/assets/img/icons/android-chrome-192x192.png new file mode 100644 index 000000000..6cda98d12 Binary files /dev/null and b/apps/auth/src/assets/img/icons/android-chrome-192x192.png differ diff --git a/apps/auth/src/assets/img/icons/android-chrome-512x512.png b/apps/auth/src/assets/img/icons/android-chrome-512x512.png new file mode 100644 index 000000000..b939a3554 Binary files /dev/null and b/apps/auth/src/assets/img/icons/android-chrome-512x512.png differ diff --git a/apps/auth/src/assets/img/icons/apple-touch-icon.png b/apps/auth/src/assets/img/icons/apple-touch-icon.png new file mode 100644 index 000000000..8647b6bb6 Binary files /dev/null and b/apps/auth/src/assets/img/icons/apple-touch-icon.png differ diff --git a/apps/auth/src/assets/img/icons/favicon-16x16.png b/apps/auth/src/assets/img/icons/favicon-16x16.png new file mode 100644 index 000000000..14611c47a Binary files /dev/null and b/apps/auth/src/assets/img/icons/favicon-16x16.png differ diff --git a/apps/auth/src/assets/img/icons/favicon-32x32.png b/apps/auth/src/assets/img/icons/favicon-32x32.png new file mode 100644 index 000000000..8cf9c6c4c Binary files /dev/null and b/apps/auth/src/assets/img/icons/favicon-32x32.png differ diff --git a/apps/auth/src/assets/img/icons/mstile-150x150.png b/apps/auth/src/assets/img/icons/mstile-150x150.png new file mode 100644 index 000000000..9e1cf551e Binary files /dev/null and b/apps/auth/src/assets/img/icons/mstile-150x150.png differ diff --git a/apps/auth/src/assets/img/icons/safari-pinned-tab.svg b/apps/auth/src/assets/img/icons/safari-pinned-tab.svg new file mode 100644 index 000000000..629c49373 --- /dev/null +++ b/apps/auth/src/assets/img/icons/safari-pinned-tab.svg @@ -0,0 +1,58 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<svg version="1.0" xmlns="http://www.w3.org/2000/svg" + width="1182.000000pt" height="1182.000000pt" viewBox="0 0 1182.000000 1182.000000" + preserveAspectRatio="xMidYMid meet"> +<metadata> +Created by potrace 1.11, written by Peter Selinger 2001-2013 +</metadata> +<g transform="translate(0.000000,1182.000000) scale(0.100000,-0.100000)" +fill="#000000" stroke="none"> +<path d="M5618 10424 c-2 -2 -40 -6 -83 -9 -44 -3 -91 -7 -105 -10 -14 -2 -52 +-6 -85 -10 -76 -8 -115 -13 -190 -27 -33 -5 -71 -12 -85 -14 -14 -3 -37 -7 +-52 -10 -15 -3 -30 -5 -33 -5 -2 0 -19 -5 -37 -10 -18 -5 -46 -12 -61 -14 -61 +-11 -259 -67 -387 -108 -464 -152 -895 -376 -1300 -677 -69 -51 -127 -96 -130 +-99 -3 -3 -32 -28 -65 -56 -33 -27 -75 -63 -94 -80 -58 -50 -334 -327 -382 +-381 -91 -105 -100 -116 -186 -224 -129 -161 -403 -582 -403 -618 0 -6 -3 -12 +-7 -14 -8 -3 -102 -189 -146 -288 -59 -132 -69 -156 -71 -165 -1 -5 -10 -28 +-20 -50 -18 -43 -125 -353 -131 -382 -2 -10 -17 -71 -34 -135 -17 -65 -33 +-128 -35 -140 -17 -79 -57 -296 -60 -331 -3 -23 -7 -58 -10 -77 -25 -173 -30 +-270 -30 -575 -1 -257 6 -422 18 -492 2 -10 7 -49 11 -88 4 -38 9 -79 11 -90 +3 -11 7 -38 10 -60 15 -103 85 -429 101 -465 3 -8 8 -25 10 -36 4 -23 88 -285 +102 -319 5 -11 18 -47 31 -80 23 -63 34 -89 69 -167 12 -26 21 -49 21 -52 0 +-13 143 -292 207 -406 115 -202 326 -502 467 -665 34 -38 72 -83 86 -99 80 +-95 381 -385 475 -457 17 -13 37 -30 45 -38 8 -7 57 -46 110 -85 52 -39 97 +-74 100 -77 20 -24 412 -269 430 -269 5 0 10 -3 12 -8 3 -8 300 -159 363 -184 +22 -9 58 -24 80 -34 22 -10 42 -18 45 -19 9 -3 45 -17 65 -25 57 -25 102 -42 +120 -46 11 -2 29 -8 40 -13 37 -18 287 -93 355 -107 14 -2 39 -9 55 -14 17 -5 +41 -12 55 -15 14 -2 45 -9 70 -15 25 -5 63 -12 85 -16 22 -3 56 -9 75 -14 19 +-5 60 -12 90 -15 30 -4 62 -9 71 -10 8 -2 42 -6 75 -10 32 -4 73 -8 89 -10 +232 -28 758 -25 977 5 37 6 88 12 115 15 26 3 59 7 73 10 14 3 54 10 90 16 36 +5 81 14 100 19 19 5 49 11 65 13 180 29 723 205 866 282 14 8 29 12 33 8 3 -3 +6 -1 6 5 0 7 5 12 11 12 12 0 211 98 304 150 105 58 119 66 185 107 79 48 253 +165 260 174 3 3 14 11 25 17 16 8 36 3 103 -29 46 -21 89 -39 96 -39 6 0 16 +-4 21 -9 8 -7 125 -60 450 -206 17 -7 65 -29 108 -49 43 -20 80 -36 82 -36 2 +0 47 -20 99 -45 53 -25 98 -45 100 -45 2 0 37 -15 78 -34 111 -51 270 -123 +367 -166 47 -21 115 -51 151 -67 36 -17 94 -43 130 -59 36 -15 68 -33 72 -39 +5 -7 8 -6 8 1 0 12 -27 66 -104 209 -24 44 -57 105 -73 135 -16 30 -36 66 -45 +80 -8 14 -38 68 -65 120 -28 52 -65 122 -83 155 -93 170 -151 277 -185 340 +-21 39 -55 101 -77 138 -21 38 -38 72 -38 76 0 3 -11 23 -25 44 -14 20 -23 37 +-20 37 4 0 -2 12 -12 28 -10 15 -32 52 -47 82 -16 30 -38 67 -48 83 -11 15 +-17 27 -14 27 4 0 -4 17 -18 38 -13 20 -35 60 -49 88 -23 46 -24 53 -11 70 7 +11 27 36 42 56 39 50 214 313 245 368 66 116 111 198 124 225 8 17 18 37 23 +45 21 33 94 190 126 270 19 47 39 94 44 105 9 18 53 133 55 145 1 3 4 12 8 20 +26 66 111 351 122 411 2 12 7 29 10 39 3 10 8 31 10 46 2 16 9 47 14 69 13 53 +21 93 27 135 2 19 8 49 13 65 4 17 7 42 7 58 -1 15 1 27 4 27 3 0 6 10 7 23 1 +29 13 145 18 177 24 141 24 685 1 900 -3 19 -7 62 -10 95 -4 33 -9 73 -12 90 +-3 16 -7 44 -9 60 -4 30 -10 69 -20 115 -3 14 -8 41 -11 60 -3 19 -14 73 -26 +120 -20 87 -26 110 -33 145 -3 11 -14 52 -25 90 -12 39 -23 77 -25 85 -21 86 +-136 397 -190 515 -40 87 -151 311 -187 375 -119 218 -349 549 -503 726 -157 +180 -353 377 -501 504 -35 30 -135 112 -149 122 -5 4 -44 34 -85 65 -94 73 +-410 283 -426 283 -3 0 -19 10 -36 21 -28 20 -381 203 -403 209 -5 1 -35 14 +-65 27 -30 14 -59 26 -65 27 -5 1 -27 10 -49 19 -21 9 -44 17 -50 17 -7 0 -16 +4 -22 9 -9 8 -218 81 -239 82 -5 1 -17 5 -26 10 -13 6 -190 56 -269 74 -52 13 +-252 54 -285 60 -71 12 -79 13 -130 20 -27 4 -61 8 -75 10 -14 2 -52 7 -85 10 +-33 4 -82 9 -110 12 -56 6 -662 13 -667 7z"/> +</g> +</svg> diff --git a/apps/auth/src/assets/img/logo-padding.png b/apps/auth/src/assets/img/logo-padding.png new file mode 100644 index 000000000..f84f50ea8 Binary files /dev/null and b/apps/auth/src/assets/img/logo-padding.png differ diff --git a/apps/auth/src/assets/img/logo.png b/apps/auth/src/assets/img/logo.png new file mode 100644 index 000000000..789f97fdc Binary files /dev/null and b/apps/auth/src/assets/img/logo.png differ diff --git a/apps/auth/src/assets/img/mail/icons/chevron-right-solid.png b/apps/auth/src/assets/img/mail/icons/chevron-right-solid.png new file mode 100644 index 000000000..a0b2581eb Binary files /dev/null and b/apps/auth/src/assets/img/mail/icons/chevron-right-solid.png differ diff --git a/apps/auth/src/assets/img/mail/icons/discord-brands.png b/apps/auth/src/assets/img/mail/icons/discord-brands.png new file mode 100644 index 000000000..1c48d038a Binary files /dev/null and b/apps/auth/src/assets/img/mail/icons/discord-brands.png differ diff --git a/apps/auth/src/assets/img/mail/icons/envelope-solid.png b/apps/auth/src/assets/img/mail/icons/envelope-solid.png new file mode 100644 index 000000000..0bc4903bc Binary files /dev/null and b/apps/auth/src/assets/img/mail/icons/envelope-solid.png differ diff --git a/apps/auth/src/assets/img/mail/icons/sign-in-alt-solid.png b/apps/auth/src/assets/img/mail/icons/sign-in-alt-solid.png new file mode 100644 index 000000000..13ef380b5 Binary files /dev/null and b/apps/auth/src/assets/img/mail/icons/sign-in-alt-solid.png differ diff --git a/apps/auth/src/assets/img/mail/icons/slack-brands.png b/apps/auth/src/assets/img/mail/icons/slack-brands.png new file mode 100644 index 000000000..c4f2f5c83 Binary files /dev/null and b/apps/auth/src/assets/img/mail/icons/slack-brands.png differ diff --git a/apps/auth/src/assets/img/mm-logo.svg b/apps/auth/src/assets/img/mm-logo.svg new file mode 100644 index 000000000..a6cffef03 --- /dev/null +++ b/apps/auth/src/assets/img/mm-logo.svg @@ -0,0 +1 @@ +<svg fill="none" height="33" viewBox="0 0 35 33" width="35" xmlns="http://www.w3.org/2000/svg"><g stroke-linecap="round" stroke-linejoin="round" stroke-width=".25"><path d="m32.9582 1-13.1341 9.7183 2.4424-5.72731z" fill="#e17726" stroke="#e17726"/><g fill="#e27625" stroke="#e27625"><path d="m2.66296 1 13.01714 9.809-2.3254-5.81802z"/><path d="m28.2295 23.5335-3.4947 5.3386 7.4829 2.0603 2.1436-7.2823z"/><path d="m1.27281 23.6501 2.13055 7.2823 7.46994-2.0603-3.48166-5.3386z"/><path d="m10.4706 14.5149-2.0786 3.1358 7.405.3369-.2469-7.969z"/><path d="m25.1505 14.5149-5.1575-4.58704-.1688 8.05974 7.4049-.3369z"/><path d="m10.8733 28.8721 4.4819-2.1639-3.8583-3.0062z"/><path d="m20.2659 26.7082 4.4689 2.1639-.6105-5.1701z"/></g><path d="m24.7348 28.8721-4.469-2.1639.3638 2.9025-.039 1.231z" fill="#d5bfb2" stroke="#d5bfb2"/><path d="m10.8732 28.8721 4.1572 1.9696-.026-1.231.3508-2.9025z" fill="#d5bfb2" stroke="#d5bfb2"/><path d="m15.1084 21.7842-3.7155-1.0884 2.6243-1.2051z" fill="#233447" stroke="#233447"/><path d="m20.5126 21.7842 1.0913-2.2935 2.6372 1.2051z" fill="#233447" stroke="#233447"/><path d="m10.8733 28.8721.6495-5.3386-4.13117.1167z" fill="#cc6228" stroke="#cc6228"/><path d="m24.0982 23.5335.6366 5.3386 3.4946-5.2219z" fill="#cc6228" stroke="#cc6228"/><path d="m27.2291 17.6507-7.405.3369.6885 3.7966 1.0913-2.2935 2.6372 1.2051z" fill="#cc6228" stroke="#cc6228"/><path d="m11.3929 20.6958 2.6242-1.2051 1.0913 2.2935.6885-3.7966-7.40495-.3369z" fill="#cc6228" stroke="#cc6228"/><path d="m8.392 17.6507 3.1049 6.0513-.1039-3.0062z" fill="#e27525" stroke="#e27525"/><path d="m24.2412 20.6958-.1169 3.0062 3.1049-6.0513z" fill="#e27525" stroke="#e27525"/><path d="m15.797 17.9876-.6886 3.7967.8704 4.4833.1949-5.9087z" fill="#e27525" stroke="#e27525"/><path d="m19.8242 17.9876-.3638 2.3584.1819 5.9216.8704-4.4833z" fill="#e27525" stroke="#e27525"/><path d="m20.5127 21.7842-.8704 4.4834.6236.4406 3.8584-3.0062.1169-3.0062z" fill="#f5841f" stroke="#f5841f"/><path d="m11.3929 20.6958.104 3.0062 3.8583 3.0062.6236-.4406-.8704-4.4834z" fill="#f5841f" stroke="#f5841f"/><path d="m20.5906 30.8417.039-1.231-.3378-.2851h-4.9626l-.3248.2851.026 1.231-4.1572-1.9696 1.4551 1.1921 2.9489 2.0344h5.0536l2.962-2.0344 1.442-1.1921z" fill="#c0ac9d" stroke="#c0ac9d"/><path d="m20.2659 26.7082-.6236-.4406h-3.6635l-.6236.4406-.3508 2.9025.3248-.2851h4.9626l.3378.2851z" fill="#161616" stroke="#161616"/><path d="m33.5168 11.3532 1.1043-5.36447-1.6629-4.98873-12.6923 9.3944 4.8846 4.1205 6.8983 2.0085 1.52-1.7752-.6626-.4795 1.0523-.9588-.8054-.622 1.0523-.8034z" fill="#763e1a" stroke="#763e1a"/><path d="m1 5.98873 1.11724 5.36447-.71451.5313 1.06527.8034-.80545.622 1.05228.9588-.66255.4795 1.51997 1.7752 6.89835-2.0085 4.8846-4.1205-12.69233-9.3944z" fill="#763e1a" stroke="#763e1a"/><path d="m32.0489 16.5234-6.8983-2.0085 2.0786 3.1358-3.1049 6.0513 4.1052-.0519h6.1318z" fill="#f5841f" stroke="#f5841f"/><path d="m10.4705 14.5149-6.89828 2.0085-2.29944 7.1267h6.11883l4.10519.0519-3.10487-6.0513z" fill="#f5841f" stroke="#f5841f"/><path d="m19.8241 17.9876.4417-7.5932 2.0007-5.4034h-8.9119l2.0006 5.4034.4417 7.5932.1689 2.3842.013 5.8958h3.6635l.013-5.8958z" fill="#f5841f" stroke="#f5841f"/></g></svg> \ No newline at end of file diff --git a/apps/auth/src/assets/img/s-logo.png b/apps/auth/src/assets/img/s-logo.png new file mode 100644 index 000000000..7bb2f2bb4 Binary files /dev/null and b/apps/auth/src/assets/img/s-logo.png differ diff --git a/apps/auth/src/assets/img/shopify-logo.png b/apps/auth/src/assets/img/shopify-logo.png new file mode 100644 index 000000000..91f963a86 Binary files /dev/null and b/apps/auth/src/assets/img/shopify-logo.png differ diff --git a/apps/auth/src/assets/img/t-logo.png b/apps/auth/src/assets/img/t-logo.png new file mode 100644 index 000000000..c24595e84 Binary files /dev/null and b/apps/auth/src/assets/img/t-logo.png differ diff --git a/apps/auth/src/assets/img/thx_landing_crater.webp b/apps/auth/src/assets/img/thx_landing_crater.webp new file mode 100644 index 000000000..6a8a97f8e Binary files /dev/null and b/apps/auth/src/assets/img/thx_landing_crater.webp differ diff --git a/apps/auth/src/assets/img/twitch-logo.png b/apps/auth/src/assets/img/twitch-logo.png new file mode 100644 index 000000000..bcc5b1024 Binary files /dev/null and b/apps/auth/src/assets/img/twitch-logo.png differ diff --git a/apps/auth/src/assets/js/logout.js b/apps/auth/src/assets/js/logout.js new file mode 100644 index 000000000..f3d9b94b2 --- /dev/null +++ b/apps/auth/src/assets/js/logout.js @@ -0,0 +1,10 @@ +function logout() { + const form = document.forms[0]; + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'logout'; + input.value = 'yes'; + form.appendChild(input); + form.submit(); +} +logout(); diff --git a/apps/auth/src/assets/js/otp.js b/apps/auth/src/assets/js/otp.js new file mode 100644 index 000000000..3eca585a5 --- /dev/null +++ b/apps/auth/src/assets/js/otp.js @@ -0,0 +1,14 @@ +import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/petite-vue/0.4.1/petite-vue.es.js'; + +createApp({ + otp: '', + isLoading: null, + onInput() { + if (this.otp.length === 5) { + this.isLoading = true; + setTimeout(() => { + this.$refs.submit.click(); + }, 1000); + } + }, +}).mount(); diff --git a/apps/auth/src/assets/js/signin.js b/apps/auth/src/assets/js/signin.js new file mode 100644 index 000000000..f88bc1983 --- /dev/null +++ b/apps/auth/src/assets/js/signin.js @@ -0,0 +1,42 @@ +import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/petite-vue/0.4.1/petite-vue.es.min.js'; + +/* eslint-disable no-undef */ + +createApp({ + isMounted: false, + alert: { variant: 'warning', message: '' }, + email: '', + isIframe: window.matchMedia('(pointer:coarse)').matches, + isMobile: window.matchMedia('(pointer:coarse)').matches, + isLoading: false, + get isDisabled() { + return this.email + ? !this.email.match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + ) + : true; + }, + onMounted() { + const isWidgetInput = document.getElementsByName('isWidget'); + const returnUrlInput = document.getElementsByName('returnUrl'); + const claimUrlInput = document.getElementsByName('claimUrl'); + const signupEmailInput = document.getElementsByName('signupEmail'); + this.email = signupEmailInput.length ? signupEmailInput[0].value : ''; + this.isWidget = isWidgetInput.length ? JSON.parse(isWidgetInput[0].value) : false; + this.returnUrl = returnUrlInput.length ? returnUrlInput[0].value : ''; + this.claimUrl = claimUrlInput.length ? claimUrlInput[0].value : ''; + this.isMounted = true; + }, + onClickReturn() { + if (window.opener) { + window.close(); + } else { + window.location.href = this.returnUrl; + } + }, + onClickSubmit() { + this.isLoading = true; + }, +}).mount(); + +/* eslint-enable no-undef */ diff --git a/apps/auth/src/assets/js/vendors/bootstrap.bundle.min.js b/apps/auth/src/assets/js/vendors/bootstrap.bundle.min.js new file mode 100644 index 000000000..3af2dc364 --- /dev/null +++ b/apps/auth/src/assets/js/vendors/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.6.1 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery)}(this,(function(t,e){"use strict";function n(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var i=n(e);function o(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function r(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}function a(){return a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t},a.apply(this,arguments)}function s(t,e){return s=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},s(t,e)}var l="transitionend";var u={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=i.default(t).css("transition-duration"),n=i.default(t).css("transition-delay"),o=parseFloat(e),r=parseFloat(n);return o||r?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){i.default(t).trigger(l)},supportsTransitionEnd:function(){return Boolean(l)},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],r=e[i],a=r&&u.isElement(r)?"element":null===(s=r)||"undefined"==typeof s?""+s:{}.toString.call(s).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(a))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+a+'" but expected type "'+o+'".')}var s},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?u.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof i.default)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=i.default.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};u.jQueryDetection(),i.default.fn.emulateTransitionEnd=function(t){var e=this,n=!1;return i.default(this).one(u.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||u.triggerTransitionEnd(e)}),t),this},i.default.event.special[u.TRANSITION_END]={bindType:l,delegateType:l,handle:function(t){if(i.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var f="bs.alert",d=i.default.fn.alert,c=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){i.default.removeData(this._element,f),this._element=null},e._getRootElement=function(t){var e=u.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=i.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=i.default.Event("close.bs.alert");return i.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(i.default(t).removeClass("show"),i.default(t).hasClass("fade")){var n=u.getTransitionDurationFromElement(t);i.default(t).one(u.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){i.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(f);o||(o=new t(this),n.data(f,o)),"close"===e&&o[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}}]),t}();i.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',c._handleDismiss(new c)),i.default.fn.alert=c._jQueryInterface,i.default.fn.alert.Constructor=c,i.default.fn.alert.noConflict=function(){return i.default.fn.alert=d,c._jQueryInterface};var h="bs.button",p=i.default.fn.button,m="active",g='[data-toggle^="button"]',_='input:not([type="hidden"])',v=".btn",b=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=i.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var o=this._element.querySelector(_);if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains(m))t=!1;else{var r=n.querySelector(".active");r&&i.default(r).removeClass(m)}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains(m)),this.shouldAvoidTriggerChange||i.default(o).trigger("change")),o.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(m)),t&&i.default(this._element).toggleClass(m))},e.dispose=function(){i.default.removeData(this._element,h),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this),r=o.data(h);r||(r=new t(this),o.data(h,r)),r.shouldAvoidTriggerChange=n,"toggle"===e&&r[e]()}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}}]),t}();i.default(document).on("click.bs.button.data-api",g,(function(t){var e=t.target,n=e;if(i.default(e).hasClass("btn")||(e=i.default(e).closest(v)[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var o=e.querySelector(_);if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||b._jQueryInterface.call(i.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",g,(function(t){var e=i.default(t.target).closest(v)[0];i.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),i.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector(_);o.checked||o.hasAttribute("checked")?i.classList.add(m):i.classList.remove(m)}for(var r=0,a=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;r<a;r++){var s=t[r];"true"===s.getAttribute("aria-pressed")?s.classList.add(m):s.classList.remove(m)}})),i.default.fn.button=b._jQueryInterface,i.default.fn.button.Constructor=b,i.default.fn.button.noConflict=function(){return i.default.fn.button=p,b._jQueryInterface};var y="carousel",E="bs.carousel",w=i.default.fn[y],T="active",C="next",S="prev",N="slid.bs.carousel",D=".active.carousel-item",A={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},k={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},I={TOUCH:"touch",PEN:"pen"},O=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide(C)},e.nextWhenVisible=function(){var t=i.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide(S)},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(u.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(D);var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)i.default(this._element).one(N,(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var o=t>n?C:S;this._slide(o,this._items[t])}},e.dispose=function(){i.default(this._element).off(".bs.carousel"),i.default.removeData(this._element,E),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=a({},A,t),u.typeCheckConfig(y,t,k),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&i.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&i.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&I[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&I[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};i.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(i.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(i.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){t.touchDeltaX=e.originalEvent.touches&&e.originalEvent.touches.length>1?0:e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),i.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n=t===C,i=t===S,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var a=(o+(t===S?-1:1))%this._items.length;return-1===a?this._items[this._items.length-1]:this._items[a]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(D)),r=i.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:o,to:n});return i.default(this._element).trigger(r),r},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));i.default(e).removeClass(T);var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&i.default(n).addClass(T)}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(D);if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var n,o,r,a=this,s=this._element.querySelector(D),l=this._getItemIndex(s),f=e||s&&this._getItemByDirection(t,s),d=this._getItemIndex(f),c=Boolean(this._interval);if(t===C?(n="carousel-item-left",o="carousel-item-next",r="left"):(n="carousel-item-right",o="carousel-item-prev",r="right"),f&&i.default(f).hasClass(T))this._isSliding=!1;else if(!this._triggerSlideEvent(f,r).isDefaultPrevented()&&s&&f){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(f),this._activeElement=f;var h=i.default.Event(N,{relatedTarget:f,direction:r,from:l,to:d});if(i.default(this._element).hasClass("slide")){i.default(f).addClass(o),u.reflow(f),i.default(s).addClass(n),i.default(f).addClass(n);var p=u.getTransitionDurationFromElement(s);i.default(s).one(u.TRANSITION_END,(function(){i.default(f).removeClass(n+" "+o).addClass(T),i.default(s).removeClass("active "+o+" "+n),a._isSliding=!1,setTimeout((function(){return i.default(a._element).trigger(h)}),0)})).emulateTransitionEnd(p)}else i.default(s).removeClass(T),i.default(f).addClass(T),this._isSliding=!1,i.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data(E),o=a({},A,i.default(this).data());"object"==typeof e&&(o=a({},o,e));var r="string"==typeof e?e:o.slide;if(n||(n=new t(this,o),i.default(this).data(E,n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if("undefined"==typeof n[r])throw new TypeError('No method named "'+r+'"');n[r]()}else o.interval&&o.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=u.getSelectorFromElement(this);if(n){var o=i.default(n)[0];if(o&&i.default(o).hasClass("carousel")){var r=a({},i.default(o).data(),i.default(this).data()),s=this.getAttribute("data-slide-to");s&&(r.interval=!1),t._jQueryInterface.call(i.default(o),r),s&&i.default(o).data(E).to(s),e.preventDefault()}}},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return A}}]),t}();i.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",O._dataApiClickHandler),i.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e<n;e++){var o=i.default(t[e]);O._jQueryInterface.call(o,o.data())}})),i.default.fn[y]=O._jQueryInterface,i.default.fn[y].Constructor=O,i.default.fn[y].noConflict=function(){return i.default.fn[y]=w,O._jQueryInterface};var x="collapse",j="bs.collapse",L=i.default.fn[x],P="show",F="collapse",R="collapsing",H="collapsed",M="width",q='[data-toggle="collapse"]',B={toggle:!0,parent:""},Q={toggle:"boolean",parent:"(string|element)"},W=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll(q)),i=0,o=n.length;i<o;i++){var r=n[i],a=u.getSelectorFromElement(r),s=[].slice.call(document.querySelectorAll(a)).filter((function(e){return e===t}));null!==a&&s.length>0&&(this._selector=a,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){i.default(this._element).hasClass(P)?this.hide():this.show()},e.show=function(){var e,n,o=this;if(!(this._isTransitioning||i.default(this._element).hasClass(P)||(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains(F)}))).length&&(e=null),e&&(n=i.default(e).not(this._selector).data(j))&&n._isTransitioning))){var r=i.default.Event("show.bs.collapse");if(i.default(this._element).trigger(r),!r.isDefaultPrevented()){e&&(t._jQueryInterface.call(i.default(e).not(this._selector),"hide"),n||i.default(e).data(j,null));var a=this._getDimension();i.default(this._element).removeClass(F).addClass(R),this._element.style[a]=0,this._triggerArray.length&&i.default(this._triggerArray).removeClass(H).attr("aria-expanded",!0),this.setTransitioning(!0);var s="scroll"+(a[0].toUpperCase()+a.slice(1)),l=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,(function(){i.default(o._element).removeClass(R).addClass("collapse show"),o._element.style[a]="",o.setTransitioning(!1),i.default(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(l),this._element.style[a]=this._element[s]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&i.default(this._element).hasClass(P)){var e=i.default.Event("hide.bs.collapse");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",u.reflow(this._element),i.default(this._element).addClass(R).removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var r=0;r<o;r++){var a=this._triggerArray[r],s=u.getSelectorFromElement(a);null!==s&&(i.default([].slice.call(document.querySelectorAll(s))).hasClass(P)||i.default(a).addClass(H).attr("aria-expanded",!1))}this.setTransitioning(!0),this._element.style[n]="";var l=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,(function(){t.setTransitioning(!1),i.default(t._element).removeClass(R).addClass(F).trigger("hidden.bs.collapse")})).emulateTransitionEnd(l)}}},e.setTransitioning=function(t){this._isTransitioning=t},e.dispose=function(){i.default.removeData(this._element,j),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(t){return(t=a({},B,t)).toggle=Boolean(t.toggle),u.typeCheckConfig(x,t,Q),t},e._getDimension=function(){return i.default(this._element).hasClass(M)?M:"height"},e._getParent=function(){var e,n=this;u.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var o='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',r=[].slice.call(e.querySelectorAll(o));return i.default(r).each((function(e,i){n._addAriaAndCollapsedClass(t._getTargetFromElement(i),[i])})),e},e._addAriaAndCollapsedClass=function(t,e){var n=i.default(t).hasClass(P);e.length&&i.default(e).toggleClass(H,!n).attr("aria-expanded",n)},t._getTargetFromElement=function(t){var e=u.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(j),r=a({},B,n.data(),"object"==typeof e&&e?e:{});if(!o&&r.toggle&&"string"==typeof e&&/show|hide/.test(e)&&(r.toggle=!1),o||(o=new t(this,r),n.data(j,o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return B}}]),t}();i.default(document).on("click.bs.collapse.data-api",q,(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var e=i.default(this),n=u.getSelectorFromElement(this),o=[].slice.call(document.querySelectorAll(n));i.default(o).each((function(){var t=i.default(this),n=t.data(j)?"toggle":e.data();W._jQueryInterface.call(t,n)}))})),i.default.fn[x]=W._jQueryInterface,i.default.fn[x].Constructor=W,i.default.fn[x].noConflict=function(){return i.default.fn[x]=L,W._jQueryInterface};var U="undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof navigator,V=function(){for(var t=["Edge","Trident","Firefox"],e=0;e<t.length;e+=1)if(U&&navigator.userAgent.indexOf(t[e])>=0)return 1;return 0}(),Y=U&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then((function(){e=!1,t()})))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout((function(){e=!1,t()}),V))}};function z(t){return t&&"[object Function]"==={}.toString.call(t)}function K(t,e){if(1!==t.nodeType)return[];var n=t.ownerDocument.defaultView.getComputedStyle(t,null);return e?n[e]:n}function X(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function G(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=K(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/(auto|scroll|overlay)/.test(n+o+i)?t:G(X(t))}function $(t){return t&&t.referenceNode?t.referenceNode:t}var J=U&&!(!window.MSInputMethodContext||!document.documentMode),Z=U&&/MSIE 10/.test(navigator.userAgent);function tt(t){return 11===t?J:10===t?Z:J||Z}function et(t){if(!t)return document.documentElement;for(var e=tt(10)?document.body:null,n=t.offsetParent||null;n===e&&t.nextElementSibling;)n=(t=t.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===K(n,"position")?et(n):n:t?t.ownerDocument.documentElement:document.documentElement}function nt(t){return null!==t.parentNode?nt(t.parentNode):t}function it(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,o=n?e:t,r=document.createRange();r.setStart(i,0),r.setEnd(o,0);var a,s,l=r.commonAncestorContainer;if(t!==l&&e!==l||i.contains(o))return"BODY"===(s=(a=l).nodeName)||"HTML"!==s&&et(a.firstElementChild)!==a?et(l):l;var u=nt(t);return u.host?it(u.host,e):it(t,nt(e).host)}function ot(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===e?"scrollTop":"scrollLeft",i=t.nodeName;if("BODY"===i||"HTML"===i){var o=t.ownerDocument.documentElement,r=t.ownerDocument.scrollingElement||o;return r[n]}return t[n]}function rt(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=ot(e,"top"),o=ot(e,"left"),r=n?-1:1;return t.top+=i*r,t.bottom+=i*r,t.left+=o*r,t.right+=o*r,t}function at(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"])+parseFloat(t["border"+i+"Width"])}function st(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],tt(10)?parseInt(n["offset"+t])+parseInt(i["margin"+("Height"===t?"Top":"Left")])+parseInt(i["margin"+("Height"===t?"Bottom":"Right")]):0)}function lt(t){var e=t.body,n=t.documentElement,i=tt(10)&&getComputedStyle(n);return{height:st("Height",e,n,i),width:st("Width",e,n,i)}}var ut=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},ft=function(){function t(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}return function(e,n,i){return n&&t(e.prototype,n),i&&t(e,i),e}}(),dt=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t},ct=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t};function ht(t){return ct({},t,{right:t.left+t.width,bottom:t.top+t.height})}function pt(t){var e={};try{if(tt(10)){e=t.getBoundingClientRect();var n=ot(t,"top"),i=ot(t,"left");e.top+=n,e.left+=i,e.bottom+=n,e.right+=i}else e=t.getBoundingClientRect()}catch(t){}var o={left:e.left,top:e.top,width:e.right-e.left,height:e.bottom-e.top},r="HTML"===t.nodeName?lt(t.ownerDocument):{},a=r.width||t.clientWidth||o.width,s=r.height||t.clientHeight||o.height,l=t.offsetWidth-a,u=t.offsetHeight-s;if(l||u){var f=K(t);l-=at(f,"x"),u-=at(f,"y"),o.width-=l,o.height-=u}return ht(o)}function mt(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=tt(10),o="HTML"===e.nodeName,r=pt(t),a=pt(e),s=G(t),l=K(e),u=parseFloat(l.borderTopWidth),f=parseFloat(l.borderLeftWidth);n&&o&&(a.top=Math.max(a.top,0),a.left=Math.max(a.left,0));var d=ht({top:r.top-a.top-u,left:r.left-a.left-f,width:r.width,height:r.height});if(d.marginTop=0,d.marginLeft=0,!i&&o){var c=parseFloat(l.marginTop),h=parseFloat(l.marginLeft);d.top-=u-c,d.bottom-=u-c,d.left-=f-h,d.right-=f-h,d.marginTop=c,d.marginLeft=h}return(i&&!n?e.contains(s):e===s&&"BODY"!==s.nodeName)&&(d=rt(d,e)),d}function gt(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.ownerDocument.documentElement,i=mt(t,n),o=Math.max(n.clientWidth,window.innerWidth||0),r=Math.max(n.clientHeight,window.innerHeight||0),a=e?0:ot(n),s=e?0:ot(n,"left"),l={top:a-i.top+i.marginTop,left:s-i.left+i.marginLeft,width:o,height:r};return ht(l)}function _t(t){var e=t.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===K(t,"position"))return!0;var n=X(t);return!!n&&_t(n)}function vt(t){if(!t||!t.parentElement||tt())return document.documentElement;for(var e=t.parentElement;e&&"none"===K(e,"transform");)e=e.parentElement;return e||document.documentElement}function bt(t,e,n,i){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},a=o?vt(t):it(t,$(e));if("viewport"===i)r=gt(a,o);else{var s=void 0;"scrollParent"===i?"BODY"===(s=G(X(e))).nodeName&&(s=t.ownerDocument.documentElement):s="window"===i?t.ownerDocument.documentElement:i;var l=mt(s,a,o);if("HTML"!==s.nodeName||_t(a))r=l;else{var u=lt(t.ownerDocument),f=u.height,d=u.width;r.top+=l.top-l.marginTop,r.bottom=f+l.top,r.left+=l.left-l.marginLeft,r.right=d+l.left}}var c="number"==typeof(n=n||0);return r.left+=c?n:n.left||0,r.top+=c?n:n.top||0,r.right-=c?n:n.right||0,r.bottom-=c?n:n.bottom||0,r}function yt(t){return t.width*t.height}function Et(t,e,n,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var a=bt(n,i,r,o),s={top:{width:a.width,height:e.top-a.top},right:{width:a.right-e.right,height:a.height},bottom:{width:a.width,height:a.bottom-e.bottom},left:{width:e.left-a.left,height:a.height}},l=Object.keys(s).map((function(t){return ct({key:t},s[t],{area:yt(s[t])})})).sort((function(t,e){return e.area-t.area})),u=l.filter((function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight})),f=u.length>0?u[0].key:l[0].key,d=t.split("-")[1];return f+(d?"-"+d:"")}function wt(t,e,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=i?vt(e):it(e,$(n));return mt(n,o,i)}function Tt(t){var e=t.ownerDocument.defaultView.getComputedStyle(t),n=parseFloat(e.marginTop||0)+parseFloat(e.marginBottom||0),i=parseFloat(e.marginLeft||0)+parseFloat(e.marginRight||0);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function Ct(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,(function(t){return e[t]}))}function St(t,e,n){n=n.split("-")[0];var i=Tt(t),o={width:i.width,height:i.height},r=-1!==["right","left"].indexOf(n),a=r?"top":"left",s=r?"left":"top",l=r?"height":"width",u=r?"width":"height";return o[a]=e[a]+e[l]/2-i[l]/2,o[s]=n===s?e[s]-i[u]:e[Ct(s)],o}function Nt(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function Dt(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex((function(t){return t.name===n}));var i=Nt(t,(function(t){return t.name===n}));return t.indexOf(i)}(t,0,n))).forEach((function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&z(n)&&(e.offsets.popper=ht(e.offsets.popper),e.offsets.reference=ht(e.offsets.reference),e=n(e,t))})),e}function At(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=wt(this.state,this.popper,this.reference,this.options.positionFixed),t.placement=Et(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.positionFixed=this.options.positionFixed,t.offsets.popper=St(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",t=Dt(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}function kt(t,e){return t.some((function(t){var n=t.name;return t.enabled&&n===e}))}function It(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i<e.length;i++){var o=e[i],r=o?""+o+n:t;if("undefined"!=typeof document.body.style[r])return r}return null}function Ot(){return this.state.isDestroyed=!0,kt(this.modifiers,"applyStyle")&&(this.popper.removeAttribute("x-placement"),this.popper.style.position="",this.popper.style.top="",this.popper.style.left="",this.popper.style.right="",this.popper.style.bottom="",this.popper.style.willChange="",this.popper.style[It("transform")]=""),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function xt(t){var e=t.ownerDocument;return e?e.defaultView:window}function jt(t,e,n,i){var o="BODY"===t.nodeName,r=o?t.ownerDocument.defaultView:t;r.addEventListener(e,n,{passive:!0}),o||jt(G(r.parentNode),e,n,i),i.push(r)}function Lt(t,e,n,i){n.updateBound=i,xt(t).addEventListener("resize",n.updateBound,{passive:!0});var o=G(t);return jt(o,"scroll",n.updateBound,n.scrollParents),n.scrollElement=o,n.eventsEnabled=!0,n}function Pt(){this.state.eventsEnabled||(this.state=Lt(this.reference,this.options,this.state,this.scheduleUpdate))}function Ft(){var t,e;this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=(t=this.reference,e=this.state,xt(t).removeEventListener("resize",e.updateBound),e.scrollParents.forEach((function(t){t.removeEventListener("scroll",e.updateBound)})),e.updateBound=null,e.scrollParents=[],e.scrollElement=null,e.eventsEnabled=!1,e))}function Rt(t){return""!==t&&!isNaN(parseFloat(t))&&isFinite(t)}function Ht(t,e){Object.keys(e).forEach((function(n){var i="";-1!==["width","height","top","right","bottom","left"].indexOf(n)&&Rt(e[n])&&(i="px"),t.style[n]=e[n]+i}))}var Mt=U&&/Firefox/i.test(navigator.userAgent);function qt(t,e,n){var i=Nt(t,(function(t){return t.name===e})),o=!!i&&t.some((function(t){return t.name===n&&t.enabled&&t.order<i.order}));if(!o){var r="`"+e+"`",a="`"+n+"`";console.warn(a+" modifier is required by "+r+" modifier in order to work, be sure to include it before "+r+"!")}return o}var Bt=["auto-start","auto","auto-end","top-start","top","top-end","right-start","right","right-end","bottom-end","bottom","bottom-start","left-end","left","left-start"],Qt=Bt.slice(3);function Wt(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=Qt.indexOf(t),i=Qt.slice(n+1).concat(Qt.slice(0,n));return e?i.reverse():i}var Ut={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var o=t.offsets,r=o.reference,a=o.popper,s=-1!==["bottom","top"].indexOf(n),l=s?"left":"top",u=s?"width":"height",f={start:dt({},l,r[l]),end:dt({},l,r[l]+r[u]-a[u])};t.offsets.popper=ct({},a,f[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n,i=e.offset,o=t.placement,r=t.offsets,a=r.popper,s=r.reference,l=o.split("-")[0];return n=Rt(+i)?[+i,0]:function(t,e,n,i){var o=[0,0],r=-1!==["right","left"].indexOf(i),a=t.split(/(\+|\-)/).map((function(t){return t.trim()})),s=a.indexOf(Nt(a,(function(t){return-1!==t.search(/,|\s/)})));a[s]&&-1===a[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,u=-1!==s?[a.slice(0,s).concat([a[s].split(l)[0]]),[a[s].split(l)[1]].concat(a.slice(s+1))]:[a];return u=u.map((function(t,i){var o=(1===i?!r:r)?"height":"width",a=!1;return t.reduce((function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,a=!0,t):a?(t[t.length-1]+=e,a=!1,t):t.concat(e)}),[]).map((function(t){return function(t,e,n,i){var o=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+o[1],a=o[2];return r?0===a.indexOf("%")?ht("%p"===a?n:i)[e]/100*r:"vh"===a||"vw"===a?("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r:r:t}(t,o,e,n)}))})),u.forEach((function(t,e){t.forEach((function(n,i){Rt(n)&&(o[e]+=n*("-"===t[i-1]?-1:1))}))})),o}(i,a,s,l),"left"===l?(a.top+=n[0],a.left-=n[1]):"right"===l?(a.top+=n[0],a.left+=n[1]):"top"===l?(a.left+=n[0],a.top-=n[1]):"bottom"===l&&(a.left+=n[0],a.top+=n[1]),t.popper=a,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||et(t.instance.popper);t.instance.reference===n&&(n=et(n));var i=It("transform"),o=t.instance.popper.style,r=o.top,a=o.left,s=o[i];o.top="",o.left="",o[i]="";var l=bt(t.instance.popper,t.instance.reference,e.padding,n,t.positionFixed);o.top=r,o.left=a,o[i]=s,e.boundaries=l;var u=e.priority,f=t.offsets.popper,d={primary:function(t){var n=f[t];return f[t]<l[t]&&!e.escapeWithReference&&(n=Math.max(f[t],l[t])),dt({},t,n)},secondary:function(t){var n="right"===t?"left":"top",i=f[n];return f[t]>l[t]&&!e.escapeWithReference&&(i=Math.min(f[n],l[t]-("right"===t?f.width:f.height))),dt({},n,i)}};return u.forEach((function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";f=ct({},f,d[e](t))})),t.offsets.popper=f,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(o),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]<r(i[l])&&(t.offsets.popper[l]=r(i[l])-n[u]),n[l]>r(i[s])&&(t.offsets.popper[l]=r(i[s])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!qt(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,a=r.popper,s=r.reference,l=-1!==["left","right"].indexOf(o),u=l?"height":"width",f=l?"Top":"Left",d=f.toLowerCase(),c=l?"left":"top",h=l?"bottom":"right",p=Tt(i)[u];s[h]-p<a[d]&&(t.offsets.popper[d]-=a[d]-(s[h]-p)),s[d]+p>a[h]&&(t.offsets.popper[d]+=s[d]+p-a[h]),t.offsets.popper=ht(t.offsets.popper);var m=s[d]+s[u]/2-p/2,g=K(t.instance.popper),_=parseFloat(g["margin"+f]),v=parseFloat(g["border"+f+"Width"]),b=m-t.offsets.popper[d]-_-v;return b=Math.max(Math.min(a[u]-p,b),0),t.arrowElement=i,t.offsets.arrow=(dt(n={},d,Math.round(b)),dt(n,c,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(kt(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=bt(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement,t.positionFixed),i=t.placement.split("-")[0],o=Ct(i),r=t.placement.split("-")[1]||"",a=[];switch(e.behavior){case"flip":a=[i,o];break;case"clockwise":a=Wt(i);break;case"counterclockwise":a=Wt(i,!0);break;default:a=e.behavior}return a.forEach((function(s,l){if(i!==s||a.length===l+1)return t;i=t.placement.split("-")[0],o=Ct(i);var u=t.offsets.popper,f=t.offsets.reference,d=Math.floor,c="left"===i&&d(u.right)>d(f.left)||"right"===i&&d(u.left)<d(f.right)||"top"===i&&d(u.bottom)>d(f.top)||"bottom"===i&&d(u.top)<d(f.bottom),h=d(u.left)<d(n.left),p=d(u.right)>d(n.right),m=d(u.top)<d(n.top),g=d(u.bottom)>d(n.bottom),_="left"===i&&h||"right"===i&&p||"top"===i&&m||"bottom"===i&&g,v=-1!==["top","bottom"].indexOf(i),b=!!e.flipVariations&&(v&&"start"===r&&h||v&&"end"===r&&p||!v&&"start"===r&&m||!v&&"end"===r&&g),y=!!e.flipVariationsByContent&&(v&&"start"===r&&p||v&&"end"===r&&h||!v&&"start"===r&&g||!v&&"end"===r&&m),E=b||y;(c||_||E)&&(t.flipped=!0,(c||_)&&(i=a[l+1]),E&&(r=function(t){return"end"===t?"start":"start"===t?"end":t}(r)),t.placement=i+(r?"-"+r:""),t.offsets.popper=ct({},t.offsets.popper,St(t.instance.popper,t.offsets.reference,t.placement)),t=Dt(t.instance.modifiers,t,"flip"))})),t},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=r[n]-(s?o[a?"width":"height"]:0),t.placement=Ct(e),t.offsets.popper=ht(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!qt(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=Nt(t.instance.modifiers,(function(t){return"preventOverflow"===t.name})).boundaries;if(e.bottom<n.top||e.left>n.right||e.top>n.bottom||e.right<n.left){if(!0===t.hide)return t;t.hide=!0,t.attributes["x-out-of-boundaries"]=""}else{if(!1===t.hide)return t;t.hide=!1,t.attributes["x-out-of-boundaries"]=!1}return t}},computeStyle:{order:850,enabled:!0,fn:function(t,e){var n=e.x,i=e.y,o=t.offsets.popper,r=Nt(t.instance.modifiers,(function(t){return"applyStyle"===t.name})).gpuAcceleration;void 0!==r&&console.warn("WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!");var a,s,l=void 0!==r?r:e.gpuAcceleration,u=et(t.instance.popper),f=pt(u),d={position:o.position},c=function(t,e){var n=t.offsets,i=n.popper,o=n.reference,r=Math.round,a=Math.floor,s=function(t){return t},l=r(o.width),u=r(i.width),f=-1!==["left","right"].indexOf(t.placement),d=-1!==t.placement.indexOf("-"),c=e?f||d||l%2==u%2?r:a:s,h=e?r:s;return{left:c(l%2==1&&u%2==1&&!d&&e?i.left-1:i.left),top:h(i.top),bottom:h(i.bottom),right:c(i.right)}}(t,window.devicePixelRatio<2||!Mt),h="bottom"===n?"top":"bottom",p="right"===i?"left":"right",m=It("transform");if(s="bottom"===h?"HTML"===u.nodeName?-u.clientHeight+c.bottom:-f.height+c.bottom:c.top,a="right"===p?"HTML"===u.nodeName?-u.clientWidth+c.right:-f.width+c.right:c.left,l&&m)d[m]="translate3d("+a+"px, "+s+"px, 0)",d[h]=0,d[p]=0,d.willChange="transform";else{var g="bottom"===h?-1:1,_="right"===p?-1:1;d[h]=s*g,d[p]=a*_,d.willChange=h+", "+p}var v={"x-placement":t.placement};return t.attributes=ct({},v,t.attributes),t.styles=ct({},d,t.styles),t.arrowStyles=ct({},t.offsets.arrow,t.arrowStyles),t},gpuAcceleration:!0,x:"bottom",y:"right"},applyStyle:{order:900,enabled:!0,fn:function(t){var e,n;return Ht(t.instance.popper,t.styles),e=t.instance.popper,n=t.attributes,Object.keys(n).forEach((function(t){!1!==n[t]?e.setAttribute(t,n[t]):e.removeAttribute(t)})),t.arrowElement&&Object.keys(t.arrowStyles).length&&Ht(t.arrowElement,t.arrowStyles),t},onLoad:function(t,e,n,i,o){var r=wt(o,e,t,n.positionFixed),a=Et(n.placement,r,e,t,n.modifiers.flip.boundariesElement,n.modifiers.flip.padding);return e.setAttribute("x-placement",a),Ht(e,{position:n.positionFixed?"fixed":"absolute"}),n},gpuAcceleration:void 0}}},Vt=function(){function t(e,n){var i=this,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};ut(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=Y(this.update.bind(this)),this.options=ct({},t.Defaults,o),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(ct({},t.Defaults.modifiers,o.modifiers)).forEach((function(e){i.options.modifiers[e]=ct({},t.Defaults.modifiers[e]||{},o.modifiers?o.modifiers[e]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(t){return ct({name:t},i.options.modifiers[t])})).sort((function(t,e){return t.order-e.order})),this.modifiers.forEach((function(t){t.enabled&&z(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)})),this.update();var r=this.options.eventsEnabled;r&&this.enableEventListeners(),this.state.eventsEnabled=r}return ft(t,[{key:"update",value:function(){return At.call(this)}},{key:"destroy",value:function(){return Ot.call(this)}},{key:"enableEventListeners",value:function(){return Pt.call(this)}},{key:"disableEventListeners",value:function(){return Ft.call(this)}}]),t}();Vt.Utils=("undefined"!=typeof window?window:global).PopperUtils,Vt.placements=Bt,Vt.Defaults=Ut;var Yt=Vt,zt="dropdown",Kt="bs.dropdown",Xt=i.default.fn[zt],Gt=new RegExp("38|40|27"),$t="disabled",Jt="show",Zt="dropdown-menu-right",te="hide.bs.dropdown",ee="hidden.bs.dropdown",ne="click.bs.dropdown.data-api",ie="keydown.bs.dropdown.data-api",oe='[data-toggle="dropdown"]',re=".dropdown-menu",ae={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},se={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},le=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!i.default(this._element).hasClass($t)){var e=i.default(this._menu).hasClass(Jt);t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||i.default(this._element).hasClass($t)||i.default(this._menu).hasClass(Jt))){var n={relatedTarget:this._element},o=i.default.Event("show.bs.dropdown",n),r=t._getParentFromElement(this._element);if(i.default(r).trigger(o),!o.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof Yt)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");var a=this._element;"parent"===this._config.reference?a=r:u.isElement(this._config.reference)&&(a=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(a=this._config.reference[0])),"scrollParent"!==this._config.boundary&&i.default(r).addClass("position-static"),this._popper=new Yt(a,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===i.default(r).closest(".navbar-nav").length&&i.default(document.body).children().on("mouseover",null,i.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),i.default(this._menu).toggleClass(Jt),i.default(r).toggleClass(Jt).trigger(i.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!i.default(this._element).hasClass($t)&&i.default(this._menu).hasClass(Jt)){var e={relatedTarget:this._element},n=i.default.Event(te,e),o=t._getParentFromElement(this._element);i.default(o).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),i.default(this._menu).toggleClass(Jt),i.default(o).toggleClass(Jt).trigger(i.default.Event(ee,e)))}},e.dispose=function(){i.default.removeData(this._element,Kt),i.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;i.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=a({},this.constructor.Default,i.default(this._element).data(),t),u.typeCheckConfig(zt,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(re))}return this._menu},e._getPlacement=function(){var t=i.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=i.default(this._menu).hasClass(Zt)?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":i.default(this._menu).hasClass(Zt)&&(e="bottom-end"),e},e._detectNavbar=function(){return i.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t._config.offset(e.offsets,t._element)),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data(Kt);if(n||(n=new t(this,"object"==typeof e?e:null),i.default(this).data(Kt,n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll(oe)),o=0,r=n.length;o<r;o++){var a=t._getParentFromElement(n[o]),s=i.default(n[o]).data(Kt),l={relatedTarget:n[o]};if(e&&"click"===e.type&&(l.clickEvent=e),s){var u=s._menu;if(i.default(a).hasClass(Jt)&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&i.default.contains(a,e.target))){var f=i.default.Event(te,l);i.default(a).trigger(f),f.isDefaultPrevented()||("ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),n[o].setAttribute("aria-expanded","false"),s._popper&&s._popper.destroy(),i.default(u).removeClass(Jt),i.default(a).removeClass(Jt).trigger(i.default.Event(ee,l)))}}}},t._getParentFromElement=function(t){var e,n=u.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(e){if(!(/input|textarea/i.test(e.target.tagName)?32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||i.default(e.target).closest(re).length):!Gt.test(e.which))&&!this.disabled&&!i.default(this).hasClass($t)){var n=t._getParentFromElement(this),o=i.default(n).hasClass(Jt);if(o||27!==e.which){if(e.preventDefault(),e.stopPropagation(),!o||27===e.which||32===e.which)return 27===e.which&&i.default(n.querySelector(oe)).trigger("focus"),void i.default(this).trigger("click");var r=[].slice.call(n.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return i.default(t).is(":visible")}));if(0!==r.length){var a=r.indexOf(e.target);38===e.which&&a>0&&a--,40===e.which&&a<r.length-1&&a++,a<0&&(a=0),r[a].focus()}}}},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return ae}},{key:"DefaultType",get:function(){return se}}]),t}();i.default(document).on(ie,oe,le._dataApiKeydownHandler).on(ie,re,le._dataApiKeydownHandler).on(ne+" keyup.bs.dropdown.data-api",le._clearMenus).on(ne,oe,(function(t){t.preventDefault(),t.stopPropagation(),le._jQueryInterface.call(i.default(this),"toggle")})).on(ne,".dropdown form",(function(t){t.stopPropagation()})),i.default.fn[zt]=le._jQueryInterface,i.default.fn[zt].Constructor=le,i.default.fn[zt].noConflict=function(){return i.default.fn[zt]=Xt,le._jQueryInterface};var ue="bs.modal",fe=i.default.fn.modal,de="modal-open",ce="fade",he="show",pe="modal-static",me="hidden.bs.modal",ge="show.bs.modal",_e="focusin.bs.modal",ve="resize.bs.modal",be="click.dismiss.bs.modal",ye="keydown.dismiss.bs.modal",Ee="mousedown.dismiss.bs.modal",we=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Te={backdrop:!0,keyboard:!0,focus:!0,show:!0},Ce={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},Se=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var e=t.prototype;return e.toggle=function(t){return this._isShown?this.hide():this.show(t)},e.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){var n=i.default.Event(ge,{relatedTarget:t});i.default(this._element).trigger(n),n.isDefaultPrevented()||(this._isShown=!0,i.default(this._element).hasClass(ce)&&(this._isTransitioning=!0),this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),i.default(this._element).on(be,'[data-dismiss="modal"]',(function(t){return e.hide(t)})),i.default(this._dialog).on(Ee,(function(){i.default(e._element).one("mouseup.dismiss.bs.modal",(function(t){i.default(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return e._showElement(t)})))}},e.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=i.default.Event("hide.bs.modal");if(i.default(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var o=i.default(this._element).hasClass(ce);if(o&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),i.default(document).off(_e),i.default(this._element).removeClass(he),i.default(this._element).off(be),i.default(this._dialog).off(Ee),o){var r=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,(function(t){return e._hideModal(t)})).emulateTransitionEnd(r)}else this._hideModal()}}},e.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return i.default(t).off(".bs.modal")})),i.default(document).off(_e),i.default.removeData(this._element,ue),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(t){return t=a({},Te,t),u.typeCheckConfig("modal",t,Ce),t},e._triggerBackdropTransition=function(){var t=this,e=i.default.Event("hidePrevented.bs.modal");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._element.scrollHeight>document.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add(pe);var o=u.getTransitionDurationFromElement(this._dialog);i.default(this._element).off(u.TRANSITION_END),i.default(this._element).one(u.TRANSITION_END,(function(){t._element.classList.remove(pe),n||i.default(t._element).one(u.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}},e._showElement=function(t){var e=this,n=i.default(this._element).hasClass(ce),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),i.default(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,n&&u.reflow(this._element),i.default(this._element).addClass(he),this._config.focus&&this._enforceFocus();var r=i.default.Event("shown.bs.modal",{relatedTarget:t}),a=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,i.default(e._element).trigger(r)};if(n){var s=u.getTransitionDurationFromElement(this._dialog);i.default(this._dialog).one(u.TRANSITION_END,a).emulateTransitionEnd(s)}else a()},e._enforceFocus=function(){var t=this;i.default(document).off(_e).on(_e,(function(e){document!==e.target&&t._element!==e.target&&0===i.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?i.default(this._element).on(ye,(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||i.default(this._element).off(ye)},e._setResizeEvent=function(){var t=this;this._isShown?i.default(window).on(ve,(function(e){return t.handleUpdate(e)})):i.default(window).off(ve)},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){i.default(document.body).removeClass(de),t._resetAdjustments(),t._resetScrollbar(),i.default(t._element).trigger(me)}))},e._removeBackdrop=function(){this._backdrop&&(i.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=i.default(this._element).hasClass(ce)?ce:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),i.default(this._backdrop).appendTo(document.body),i.default(this._element).on(be,(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),n&&u.reflow(this._backdrop),i.default(this._backdrop).addClass(he),!t)return;if(!n)return void t();var o=u.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(u.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){i.default(this._backdrop).removeClass(he);var r=function(){e._removeBackdrop(),t&&t()};if(i.default(this._element).hasClass(ce)){var a=u.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(u.TRANSITION_END,r).emulateTransitionEnd(a)}else r()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(we)),n=[].slice.call(document.querySelectorAll(".sticky-top"));i.default(e).each((function(e,n){var o=n.style.paddingRight,r=i.default(n).css("padding-right");i.default(n).data("padding-right",o).css("padding-right",parseFloat(r)+t._scrollbarWidth+"px")})),i.default(n).each((function(e,n){var o=n.style.marginRight,r=i.default(n).css("margin-right");i.default(n).data("margin-right",o).css("margin-right",parseFloat(r)-t._scrollbarWidth+"px")}));var o=document.body.style.paddingRight,r=i.default(document.body).css("padding-right");i.default(document.body).data("padding-right",o).css("padding-right",parseFloat(r)+this._scrollbarWidth+"px")}i.default(document.body).addClass(de)},e._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(we));i.default(t).each((function(t,e){var n=i.default(e).data("padding-right");i.default(e).removeData("padding-right"),e.style.paddingRight=n||""}));var e=[].slice.call(document.querySelectorAll(".sticky-top"));i.default(e).each((function(t,e){var n=i.default(e).data("margin-right");"undefined"!=typeof n&&i.default(e).css("margin-right",n).removeData("margin-right")}));var n=i.default(document.body).data("padding-right");i.default(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this).data(ue),r=a({},Te,i.default(this).data(),"object"==typeof e&&e?e:{});if(o||(o=new t(this,r),i.default(this).data(ue,o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](n)}else r.show&&o.show(n)}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return Te}}]),t}();i.default(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var e,n=this,o=u.getSelectorFromElement(this);o&&(e=document.querySelector(o));var r=i.default(e).data(ue)?"toggle":a({},i.default(e).data(),i.default(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var s=i.default(e).one(ge,(function(t){t.isDefaultPrevented()||s.one(me,(function(){i.default(n).is(":visible")&&n.focus()}))}));Se._jQueryInterface.call(i.default(e),r,this)})),i.default.fn.modal=Se._jQueryInterface,i.default.fn.modal.Constructor=Se,i.default.fn.modal.noConflict=function(){return i.default.fn.modal=fe,Se._jQueryInterface};var Ne=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],De=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,Ae=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function ke(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),r=[].slice.call(i.body.querySelectorAll("*")),a=function(t,n){var i=r[t],a=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var s=[].slice.call(i.attributes),l=[].concat(e["*"]||[],e[a]||[]);s.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===Ne.indexOf(n)||Boolean(De.test(t.nodeValue)||Ae.test(t.nodeValue));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,r=i.length;o<r;o++)if(i[o].test(n))return!0;return!1})(t,l)||i.removeAttribute(t.nodeName)}))},s=0,l=r.length;s<l;s++)a(s);return i.body.innerHTML}var Ie="tooltip",Oe="bs.tooltip",xe=i.default.fn.tooltip,je=new RegExp("(^|\\s)bs-tooltip\\S+","g"),Le=["sanitize","whiteList","sanitizeFn"],Pe="fade",Fe="show",Re="show",He="out",Me="hover",qe="focus",Be={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Qe={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},We={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},Ue={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},Ve=function(){function t(t,e){if("undefined"==typeof Yt)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=i.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(i.default(this.getTipElement()).hasClass(Fe))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),i.default.removeData(this.element,this.constructor.DATA_KEY),i.default(this.element).off(this.constructor.EVENT_KEY),i.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&i.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===i.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=i.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){i.default(this.element).trigger(e);var n=u.findShadowRoot(this.element),o=i.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!o)return;var r=this.getTipElement(),a=u.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&i.default(r).addClass(Pe);var s="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,l=this._getAttachment(s);this.addAttachmentClass(l);var f=this._getContainer();i.default(r).data(this.constructor.DATA_KEY,this),i.default.contains(this.element.ownerDocument.documentElement,this.tip)||i.default(r).appendTo(f),i.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Yt(this.element,r,this._getPopperConfig(l)),i.default(r).addClass(Fe),i.default(r).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&i.default(document.body).children().on("mouseover",null,i.default.noop);var d=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,i.default(t.element).trigger(t.constructor.Event.SHOWN),e===He&&t._leave(null,t)};if(i.default(this.tip).hasClass(Pe)){var c=u.getTransitionDurationFromElement(this.tip);i.default(this.tip).one(u.TRANSITION_END,d).emulateTransitionEnd(c)}else d()}},e.hide=function(t){var e=this,n=this.getTipElement(),o=i.default.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==Re&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),i.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(i.default(this.element).trigger(o),!o.isDefaultPrevented()){if(i.default(n).removeClass(Fe),"ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,i.default(this.tip).hasClass(Pe)){var a=u.getTransitionDurationFromElement(n);i.default(n).one(u.TRANSITION_END,r).emulateTransitionEnd(a)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(i.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),i.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=ke(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?i.default(e).parent().is(t)||t.empty().append(e):t.text(i.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t.config.offset(e.offsets,t.element)),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:u.isElement(this.config.container)?i.default(this.config.container):i.default(document).find(this.config.container)},e._getAttachment=function(t){return Be[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)i.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n=e===Me?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o=e===Me?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;i.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},i.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?qe:Me]=!0),i.default(e.getTipElement()).hasClass(Fe)||e._hoverState===Re?e._hoverState=Re:(clearTimeout(e._timeout),e._hoverState=Re,e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){e._hoverState===Re&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?qe:Me]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=He,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){e._hoverState===He&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=i.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Le.indexOf(t)&&delete e[t]})),"number"==typeof(t=a({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),u.typeCheckConfig(Ie,t,this.constructor.DefaultType),t.sanitize&&(t.template=ke(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(je);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(i.default(t).removeClass(Pe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(Oe),r="object"==typeof e&&e;if((o||!/dispose|hide/.test(e))&&(o||(o=new t(this,r),n.data(Oe,o)),"string"==typeof e)){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return Qe}},{key:"NAME",get:function(){return Ie}},{key:"DATA_KEY",get:function(){return Oe}},{key:"Event",get:function(){return Ue}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return We}}]),t}();i.default.fn.tooltip=Ve._jQueryInterface,i.default.fn.tooltip.Constructor=Ve,i.default.fn.tooltip.noConflict=function(){return i.default.fn.tooltip=xe,Ve._jQueryInterface};var Ye="bs.popover",ze=i.default.fn.popover,Ke=new RegExp("(^|\\s)bs-popover\\S+","g"),Xe=a({},Ve.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),Ge=a({},Ve.DefaultType,{content:"(string|element|function)"}),$e={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},Je=function(t){var e,n;function o(){return t.apply(this,arguments)||this}n=t,(e=o).prototype=Object.create(n.prototype),e.prototype.constructor=e,s(e,n);var a=o.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},a.setContent=function(){var t=i.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(Ke);null!==e&&e.length>0&&t.removeClass(e.join(""))},o._jQueryInterface=function(t){return this.each((function(){var e=i.default(this).data(Ye),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new o(this,n),i.default(this).data(Ye,e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},r(o,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return Xe}},{key:"NAME",get:function(){return"popover"}},{key:"DATA_KEY",get:function(){return Ye}},{key:"Event",get:function(){return $e}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Ge}}]),o}(Ve);i.default.fn.popover=Je._jQueryInterface,i.default.fn.popover.Constructor=Je,i.default.fn.popover.noConflict=function(){return i.default.fn.popover=ze,Je._jQueryInterface};var Ze="scrollspy",tn="bs.scrollspy",en=i.default.fn[Ze],nn="active",on="position",rn=".nav, .list-group",an={offset:10,method:"auto",target:""},sn={offset:"number",method:"string",target:"(string|element)"},ln=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,i.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":on,n="auto"===this._config.method?e:this._config.method,o=n===on?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,r=u.getSelectorFromElement(t);if(r&&(e=document.querySelector(r)),e){var a=e.getBoundingClientRect();if(a.width||a.height)return[i.default(e)[n]().top+o,r]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){i.default.removeData(this._element,tn),i.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=a({},an,"object"==typeof t&&t?t:{})).target&&u.isElement(t.target)){var e=i.default(t.target).attr("id");e||(e=u.getUID(Ze),i.default(t.target).attr("id",e)),t.target="#"+e}return u.typeCheckConfig(Ze,t,sn),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),n=i.default([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass("dropdown-item")?(n.closest(".dropdown").find(".dropdown-toggle").addClass(nn),n.addClass(nn)):(n.addClass(nn),n.parents(rn).prev(".nav-link, .list-group-item").addClass(nn),n.parents(rn).prev(".nav-item").children(".nav-link").addClass(nn)),i.default(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},e._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains(nn)})).forEach((function(t){return t.classList.remove(nn)}))},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data(tn);if(n||(n=new t(this,"object"==typeof e&&e),i.default(this).data(tn,n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return an}}]),t}();i.default(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),e=t.length;e--;){var n=i.default(t[e]);ln._jQueryInterface.call(n,n.data())}})),i.default.fn[Ze]=ln._jQueryInterface,i.default.fn[Ze].Constructor=ln,i.default.fn[Ze].noConflict=function(){return i.default.fn[Ze]=en,ln._jQueryInterface};var un="bs.tab",fn=i.default.fn.tab,dn="active",cn="fade",hn="show",pn=".active",mn="> li > .active",gn=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&i.default(this._element).hasClass(dn)||i.default(this._element).hasClass("disabled"))){var e,n,o=i.default(this._element).closest(".nav, .list-group")[0],r=u.getSelectorFromElement(this._element);if(o){var a="UL"===o.nodeName||"OL"===o.nodeName?mn:pn;n=(n=i.default.makeArray(i.default(o).find(a)))[n.length-1]}var s=i.default.Event("hide.bs.tab",{relatedTarget:this._element}),l=i.default.Event("show.bs.tab",{relatedTarget:n});if(n&&i.default(n).trigger(s),i.default(this._element).trigger(l),!l.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,o);var f=function(){var e=i.default.Event("hidden.bs.tab",{relatedTarget:t._element}),o=i.default.Event("shown.bs.tab",{relatedTarget:n});i.default(n).trigger(e),i.default(t._element).trigger(o)};e?this._activate(e,e.parentNode,f):f()}}},e.dispose=function(){i.default.removeData(this._element,un),this._element=null},e._activate=function(t,e,n){var o=this,r=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?i.default(e).children(pn):i.default(e).find(mn))[0],a=n&&r&&i.default(r).hasClass(cn),s=function(){return o._transitionComplete(t,r,n)};if(r&&a){var l=u.getTransitionDurationFromElement(r);i.default(r).removeClass(hn).one(u.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._transitionComplete=function(t,e,n){if(e){i.default(e).removeClass(dn);var o=i.default(e.parentNode).find("> .dropdown-menu .active")[0];o&&i.default(o).removeClass(dn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}i.default(t).addClass(dn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),u.reflow(t),t.classList.contains(cn)&&t.classList.add(hn);var r=t.parentNode;if(r&&"LI"===r.nodeName&&(r=r.parentNode),r&&i.default(r).hasClass("dropdown-menu")){var a=i.default(t).closest(".dropdown")[0];if(a){var s=[].slice.call(a.querySelectorAll(".dropdown-toggle"));i.default(s).addClass(dn)}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(un);if(o||(o=new t(this),n.data(un,o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}}]),t}();i.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),gn._jQueryInterface.call(i.default(this),"show")})),i.default.fn.tab=gn._jQueryInterface,i.default.fn.tab.Constructor=gn,i.default.fn.tab.noConflict=function(){return i.default.fn.tab=fn,gn._jQueryInterface};var _n="bs.toast",vn=i.default.fn.toast,bn="hide",yn="show",En="showing",wn="click.dismiss.bs.toast",Tn={animation:!0,autohide:!0,delay:500},Cn={animation:"boolean",autohide:"boolean",delay:"number"},Sn=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=i.default.Event("show.bs.toast");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove(En),t._element.classList.add(yn),i.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove(bn),u.reflow(this._element),this._element.classList.add(En),this._config.animation){var o=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,n).emulateTransitionEnd(o)}else n()}},e.hide=function(){if(this._element.classList.contains(yn)){var t=i.default.Event("hide.bs.toast");i.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains(yn)&&this._element.classList.remove(yn),i.default(this._element).off(wn),i.default.removeData(this._element,_n),this._element=null,this._config=null},e._getConfig=function(t){return t=a({},Tn,i.default(this._element).data(),"object"==typeof t&&t?t:{}),u.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;i.default(this._element).on(wn,'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add(bn),i.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove(yn),this._config.animation){var n=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(_n);if(o||(o=new t(this,"object"==typeof e&&e),n.data(_n,o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](this)}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"DefaultType",get:function(){return Cn}},{key:"Default",get:function(){return Tn}}]),t}();i.default.fn.toast=Sn._jQueryInterface,i.default.fn.toast.Constructor=Sn,i.default.fn.toast.noConflict=function(){return i.default.fn.toast=vn,Sn._jQueryInterface},t.Alert=c,t.Button=b,t.Carousel=O,t.Collapse=W,t.Dropdown=le,t.Modal=Se,t.Popover=Je,t.Scrollspy=ln,t.Tab=gn,t.Toast=Sn,t.Tooltip=Ve,t.Util=u,Object.defineProperty(t,"__esModule",{value:!0})})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/apps/auth/src/assets/js/vendors/detect.min.js b/apps/auth/src/assets/js/vendors/detect.min.js new file mode 100644 index 000000000..71cedfebd --- /dev/null +++ b/apps/auth/src/assets/js/vendors/detect.min.js @@ -0,0 +1,43 @@ +!(function (e) { + if ('object' == typeof exports && 'undefined' != typeof module) module.exports = e(); + else if ('function' == typeof define && define.amd) define([], e); + else { + ('undefined' != typeof window + ? window + : 'undefined' != typeof global + ? global + : 'undefined' != typeof self + ? self + : this + ).detectEthereumProvider = e(); + } +})(function () { + return function ({ mustBeMetaMask: e = !1, silent: t = !1, timeout: o = 3e3 } = {}) { + !(function () { + if ('boolean' != typeof e) + throw new Error("@metamask/detect-provider: Expected option 'mustBeMetaMask' to be a boolean."); + if ('boolean' != typeof t) + throw new Error("@metamask/detect-provider: Expected option 'silent' to be a boolean."); + if ('number' != typeof o) + throw new Error("@metamask/detect-provider: Expected option 'timeout' to be a number."); + })(); + let n = !1; + return new Promise((i) => { + function r() { + if (n) return; + (n = !0), window.removeEventListener('ethereum#initialized', r); + const { ethereum: o } = window; + if (!o || (e && !o.isMetaMask)) { + const n = e && o ? 'Non-MetaMask window.ethereum detected.' : 'Unable to detect window.ethereum.'; + !t && console.error('@metamask/detect-provider:', n), i(null); + } else i(o); + } + window.ethereum + ? r() + : (window.addEventListener('ethereum#initialized', r, { once: !0 }), + setTimeout(() => { + r(); + }, o)); + }); + }; +}); diff --git a/apps/auth/src/assets/js/vendors/fontawesome.06b7267748.js b/apps/auth/src/assets/js/vendors/fontawesome.06b7267748.js new file mode 100644 index 000000000..07543be7d --- /dev/null +++ b/apps/auth/src/assets/js/vendors/fontawesome.06b7267748.js @@ -0,0 +1,2 @@ +window.FontAwesomeKitConfig = {"asyncLoading":{"enabled":false},"autoA11y":{"enabled":true},"baseUrl":"https://ka-f.fontawesome.com","baseUrlKit":"https://kit.fontawesome.com","detectConflictsUntil":null,"iconUploads":{},"id":7947482,"license":"free","method":"css","minify":{"enabled":true},"token":"06b7267748","v4FontFaceShim":{"enabled":true},"v4shim":{"enabled":true},"v5FontFaceShim":{"enabled":false},"version":"5.15.4"}; +!function(t){"function"==typeof define&&define.amd?define("kit-loader",t):t()}((function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function n(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,o)}return n}function o(t){for(var o=1;o<arguments.length;o++){var r=null!=arguments[o]?arguments[o]:{};o%2?n(Object(r),!0).forEach((function(n){e(t,n,r[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):n(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function r(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var n=[],o=!0,r=!1,i=void 0;try{for(var c,a=t[Symbol.iterator]();!(o=(c=a.next()).done)&&(n.push(c.value),!e||n.length!==e);o=!0);}catch(t){r=!0,i=t}finally{try{o||null==a.return||a.return()}finally{if(r)throw i}}return n}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return i(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function c(t,e){var n=e&&e.addOn||"",o=e&&e.baseFilename||t.license+n,r=e&&e.minify?".min":"",i=e&&e.fileSuffix||t.method,c=e&&e.subdir||t.method;return t.baseUrl+"/releases/"+("latest"===t.version?"latest":"v".concat(t.version))+"/"+c+"/"+o+r+"."+i}function a(t){return t.baseUrlKit+"/"+t.token+"/"+t.id+"/kit-upload.css"}function u(t,e){var n=e||["fa"],o="."+Array.prototype.join.call(n,",."),r=t.querySelectorAll(o);Array.prototype.forEach.call(r,(function(e){var n=e.getAttribute("title");e.setAttribute("aria-hidden","true");var o=!e.nextElementSibling||!e.nextElementSibling.classList.contains("sr-only");if(n&&o){var r=t.createElement("span");r.innerHTML=n,r.classList.add("sr-only"),e.parentNode.insertBefore(r,e.nextSibling)}}))}var f,s=function(){},d="undefined"!=typeof global&&void 0!==global.process&&"function"==typeof global.process.emit,l="undefined"==typeof setImmediate?setTimeout:setImmediate,h=[];function m(){for(var t=0;t<h.length;t++)h[t][0](h[t][1]);h=[],f=!1}function p(t,e){h.push([t,e]),f||(f=!0,l(m,0))}function v(t){var e=t.owner,n=e._state,o=e._data,r=t[n],i=t.then;if("function"==typeof r){n="fulfilled";try{o=r(o)}catch(t){w(i,t)}}y(i,o)||("fulfilled"===n&&b(i,o),"rejected"===n&&w(i,o))}function y(e,n){var o;try{if(e===n)throw new TypeError("A promises callback cannot return that same promise.");if(n&&("function"==typeof n||"object"===t(n))){var r=n.then;if("function"==typeof r)return r.call(n,(function(t){o||(o=!0,n===t?g(e,t):b(e,t))}),(function(t){o||(o=!0,w(e,t))})),!0}}catch(t){return o||w(e,t),!0}return!1}function b(t,e){t!==e&&y(t,e)||g(t,e)}function g(t,e){"pending"===t._state&&(t._state="settled",t._data=e,p(S,t))}function w(t,e){"pending"===t._state&&(t._state="settled",t._data=e,p(O,t))}function A(t){t._then=t._then.forEach(v)}function S(t){t._state="fulfilled",A(t)}function O(t){t._state="rejected",A(t),!t._handled&&d&&global.process.emit("unhandledRejection",t._data,t)}function j(t){global.process.emit("rejectionHandled",t)}function E(t){if("function"!=typeof t)throw new TypeError("Promise resolver "+t+" is not a function");if(this instanceof E==!1)throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._then=[],function(t,e){function n(t){w(e,t)}try{t((function(t){b(e,t)}),n)}catch(t){n(t)}}(t,this)}E.prototype={constructor:E,_state:"pending",_then:null,_data:void 0,_handled:!1,then:function(t,e){var n={owner:this,then:new this.constructor(s),fulfilled:t,rejected:e};return!e&&!t||this._handled||(this._handled=!0,"rejected"===this._state&&d&&p(j,this)),"fulfilled"===this._state||"rejected"===this._state?p(v,n):this._then.push(n),n.then},catch:function(t){return this.then(null,t)}},E.all=function(t){if(!Array.isArray(t))throw new TypeError("You must pass an array to Promise.all().");return new E((function(e,n){var o=[],r=0;function i(t){return r++,function(n){o[t]=n,--r||e(o)}}for(var c,a=0;a<t.length;a++)(c=t[a])&&"function"==typeof c.then?c.then(i(a),n):o[a]=c;r||e(o)}))},E.race=function(t){if(!Array.isArray(t))throw new TypeError("You must pass an array to Promise.race().");return new E((function(e,n){for(var o,r=0;r<t.length;r++)(o=t[r])&&"function"==typeof o.then?o.then(e,n):e(o)}))},E.resolve=function(e){return e&&"object"===t(e)&&e.constructor===E?e:new E((function(t){t(e)}))},E.reject=function(t){return new E((function(e,n){n(t)}))};var _="function"==typeof Promise?Promise:E;function F(t,e){var n=e.fetch,o=e.XMLHttpRequest,r=e.token,i=t;return"URLSearchParams"in window?(i=new URL(t)).searchParams.set("token",r):i=i+"?token="+encodeURIComponent(r),i=i.toString(),new _((function(t,e){if("function"==typeof n)n(i,{mode:"cors",cache:"default"}).then((function(t){if(t.ok)return t.text();throw new Error("")})).then((function(e){t(e)})).catch(e);else if("function"==typeof o){var r=new o;r.addEventListener("loadend",(function(){this.responseText?t(this.responseText):e(new Error(""))}));["abort","error","timeout"].map((function(t){r.addEventListener(t,(function(){e(new Error(""))}))})),r.open("GET",i),r.send()}else{e(new Error(""))}}))}function P(t,e,n){var o=t;return[[/(url\("?)\.\.\/\.\.\/\.\./g,function(t,n){return"".concat(n).concat(e)}],[/(url\("?)\.\.\/webfonts/g,function(t,o){return"".concat(o).concat(e,"/releases/v").concat(n,"/webfonts")}],[/(url\("?)https:\/\/kit-free([^.])*\.fontawesome\.com/g,function(t,n){return"".concat(n).concat(e)}]].forEach((function(t){var e=r(t,2),n=e[0],i=e[1];o=o.replace(n,i)})),o}function C(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=e.document||r,i=u.bind(u,r,["fa","fab","fas","far","fal","fad","fak"]),f=Object.keys(t.iconUploads||{}).length>0;t.autoA11y.enabled&&n(i);var s=[{id:"fa-main",addOn:void 0}];t.v4shim&&t.v4shim.enabled&&s.push({id:"fa-v4-shims",addOn:"-v4-shims"}),t.v5FontFaceShim&&t.v5FontFaceShim.enabled&&s.push({id:"fa-v5-font-face",addOn:"-v5-font-face"}),t.v4FontFaceShim&&t.v4FontFaceShim.enabled&&s.push({id:"fa-v4-font-face",addOn:"-v4-font-face"}),f&&s.push({id:"fa-kit-upload",customCss:!0});var d=s.map((function(n){return new _((function(r,i){F(n.customCss?a(t):c(t,{addOn:n.addOn,minify:t.minify.enabled}),e).then((function(i){r(U(i,o(o({},e),{},{baseUrl:t.baseUrl,version:t.version,id:n.id,contentFilter:function(t,e){return P(t,e.baseUrl,e.version)}})))})).catch(i)}))}));return _.all(d)}function U(t,e){var n=e.contentFilter||function(t,e){return t},o=document.createElement("style"),r=document.createTextNode(n(t,e));return o.appendChild(r),o.media="all",e.id&&o.setAttribute("id",e.id),e&&e.detectingConflicts&&e.detectionIgnoreAttr&&o.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),o}function k(t,e){e.autoA11y=t.autoA11y.enabled,"pro"===t.license&&(e.autoFetchSvg=!0,e.fetchSvgFrom=t.baseUrl+"/releases/"+("latest"===t.version?"latest":"v".concat(t.version))+"/svgs",e.fetchUploadedSvgFrom=t.uploadsUrl);var n=[];return t.v4shim.enabled&&n.push(new _((function(n,r){F(c(t,{addOn:"-v4-shims",minify:t.minify.enabled}),e).then((function(t){n(I(t,o(o({},e),{},{id:"fa-v4-shims"})))})).catch(r)}))),n.push(new _((function(n,r){F(c(t,{minify:t.minify.enabled}),e).then((function(t){var r=I(t,o(o({},e),{},{id:"fa-main"}));n(function(t,e){var n=e&&void 0!==e.autoFetchSvg?e.autoFetchSvg:void 0,o=e&&void 0!==e.autoA11y?e.autoA11y:void 0;void 0!==o&&t.setAttribute("data-auto-a11y",o?"true":"false");n&&(t.setAttributeNode(document.createAttribute("data-auto-fetch-svg")),t.setAttribute("data-fetch-svg-from",e.fetchSvgFrom),t.setAttribute("data-fetch-uploaded-svg-from",e.fetchUploadedSvgFrom));return t}(r,e))})).catch(r)}))),_.all(n)}function I(t,e){var n=document.createElement("SCRIPT"),o=document.createTextNode(t);return n.appendChild(o),n.referrerPolicy="strict-origin",e.id&&n.setAttribute("id",e.id),e&&e.detectingConflicts&&e.detectionIgnoreAttr&&n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),n}function L(t){var e,n=[],o=document,r=o.documentElement.doScroll,i=(r?/^loaded|^c/:/^loaded|^i|^c/).test(o.readyState);i||o.addEventListener("DOMContentLoaded",e=function(){for(o.removeEventListener("DOMContentLoaded",e),i=1;e=n.shift();)e()}),i?setTimeout(t,0):n.push(t)}function T(t){"undefined"!=typeof MutationObserver&&new MutationObserver(t).observe(document,{childList:!0,subtree:!0})}try{if(window.FontAwesomeKitConfig){var x=window.FontAwesomeKitConfig,M={detectingConflicts:x.detectConflictsUntil&&new Date<=new Date(x.detectConflictsUntil),detectionIgnoreAttr:"data-fa-detection-ignore",fetch:window.fetch,token:x.token,XMLHttpRequest:window.XMLHttpRequest,document:document},D=document.currentScript,N=D?D.parentElement:document.head;(function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"js"===t.method?k(t,e):"css"===t.method?C(t,e,(function(t){L(t),T(t)})):void 0})(x,M).then((function(t){t.map((function(t){try{N.insertBefore(t,D?D.nextSibling:null)}catch(e){N.appendChild(t)}})),M.detectingConflicts&&D&&L((function(){D.setAttributeNode(document.createAttribute(M.detectionIgnoreAttr));var t=function(t,e){var n=document.createElement("script");return e&&e.detectionIgnoreAttr&&n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),n.src=c(t,{baseFilename:"conflict-detection",fileSuffix:"js",subdir:"js",minify:t.minify.enabled}),n}(x,M);document.body.appendChild(t)}))})).catch((function(t){console.error("".concat("Font Awesome Kit:"," ").concat(t))}))}}catch(t){console.error("".concat("Font Awesome Kit:"," ").concat(t))}})); diff --git a/apps/auth/src/assets/js/vendors/jquery.slim.min.js b/apps/auth/src/assets/js/vendors/jquery.slim.min.js new file mode 100644 index 000000000..36b4e1a13 --- /dev/null +++ b/apps/auth/src/assets/js/vendors/jquery.slim.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(g,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,v=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),m={},b=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},w=g.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function C(e,t,n){var r,i,o=(n=n||w).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function T(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector",E=function(e,t){return new E.fn.init(e,t)};function d(e){var t=!!e&&"length"in e&&e.length,n=T(e);return!b(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}E.fn=E.prototype={jquery:f,constructor:E,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=E.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return E.each(this,e)},map:function(n){return this.pushStack(E.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(E.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(E.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},E.extend=E.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||b(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(E.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||E.isPlainObject(n)?n:{},i=!1,a[t]=E.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},E.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=y.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){C(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(d(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(d(Object(e))?E.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(d(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return v(a)},guid:1,support:m}),"function"==typeof Symbol&&(E.fn[Symbol.iterator]=t[Symbol.iterator]),E.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var p=function(n){var e,p,x,o,i,h,f,g,w,u,l,C,T,a,E,v,s,c,y,A="sizzle"+1*new Date,d=n.document,N=0,r=0,m=ue(),b=ue(),S=ue(),k=ue(),D=function(e,t){return e===t&&(l=!0),0},L={}.hasOwnProperty,t=[],j=t.pop,q=t.push,O=t.push,P=t.slice,H=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},I="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",R="[\\x20\\t\\r\\n\\f]",B="(?:\\\\[\\da-fA-F]{1,6}"+R+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",M="\\["+R+"*("+B+")(?:"+R+"*([*^$|!~]?=)"+R+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+B+"))|)"+R+"*\\]",W=":("+B+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",F=new RegExp(R+"+","g"),$=new RegExp("^"+R+"+|((?:^|[^\\\\])(?:\\\\.)*)"+R+"+$","g"),z=new RegExp("^"+R+"*,"+R+"*"),_=new RegExp("^"+R+"*([>+~]|"+R+")"+R+"*"),U=new RegExp(R+"|>"),V=new RegExp(W),X=new RegExp("^"+B+"$"),Q={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),TAG:new RegExp("^("+B+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+R+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){C()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{O.apply(t=P.call(d.childNodes),d.childNodes),t[d.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){q.apply(e,P.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(C(e),e=e||T,E)){if(11!==d&&(u=Z.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return O.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&p.getElementsByClassName&&e.getElementsByClassName)return O.apply(n,e.getElementsByClassName(i)),n}if(p.qsa&&!k[t+" "]&&(!v||!v.test(t))&&(1!==d||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===d&&(U.test(t)||_.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&p.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=A)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+be(l[o]);c=l.join(",")}try{return O.apply(n,f.querySelectorAll(c)),n}catch(e){k(t,!0)}finally{s===A&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[A]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in p=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},C=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:d;return r!=T&&9===r.nodeType&&r.documentElement&&(a=(T=r).documentElement,E=!i(T),d!=T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),p.scope=ce(function(e){return a.appendChild(e).appendChild(T.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),p.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=J.test(T.getElementsByClassName),p.getById=ce(function(e){return a.appendChild(e).id=A,!T.getElementsByName||!T.getElementsByName(A).length}),p.getById?(x.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=p.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},x.find.CLASS=p.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(p.qsa=J.test(T.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+A+"'></a><select id='"+A+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+R+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+A+"-]").length||v.push("~="),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+R+"*name"+R+"*="+R+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+A+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(p.matchesSelector=J.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=J.test(a.compareDocumentPosition),y=t||J.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e==T||e.ownerDocument==d&&y(d,e)?-1:t==T||t.ownerDocument==d&&y(d,t)?1:u?H(u,e)-H(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==T?-1:t==T?1:i?-1:o?1:u?H(u,e)-H(u,t):0;if(i===o)return de(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?de(a[r],s[r]):a[r]==d?-1:s[r]==d?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(C(e),p.matchesSelector&&E&&!k[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){k(t,!0)}return 0<se(t,T,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=T&&C(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=T&&C(e);var n=x.attrHandle[t.toLowerCase()],r=n&&L.call(x.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:p.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!p.detectDuplicates,u=!p.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(x=se.selectors={cacheLength:50,createPseudo:le,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(F," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),b="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=b&&e.nodeName.toLowerCase(),d=!n&&!b,p=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(b?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&d){p=(s=(r=(i=(o=(a=c)[A]||(a[A]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===N&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(p=s=0)||u.pop())if(1===a.nodeType&&++p&&a===e){i[h]=[N,s,p];break}}else if(d&&(p=s=(r=(i=(o=(a=e)[A]||(a[A]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===N&&r[1]),!1===p)while(a=++s&&a&&a[l]||(p=s=0)||u.pop())if((b?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++p&&(d&&((i=(o=a[A]||(a[A]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[N,p]),a===e))break;return(p-=v)===g||p%g==0&&0<=p/g}}},PSEUDO:function(e,o){var t,a=x.pseudos[e]||x.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[A]?a(o):1<a.length?(t=[e,e,"",o],x.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=H(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[A]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return X.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===T.activeElement&&(!T.hasFocus||T.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!x.pseudos.empty(e)},header:function(e){return K.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=x.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})x.pseudos[e]=pe(e);for(e in{submit:!0,reset:!0})x.pseudos[e]=he(e);function me(){}function be(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function xe(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,d=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[N,d];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[A]||(e[A]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===N&&r[1]===d)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Ce(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Te(p,h,g,v,y,e){return v&&!v[A]&&(v=Te(v)),y&&!y[A]&&(y=Te(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!p||!e&&h?c:Ce(c,s,p,n,r),d=g?y||(e?p:l||v)?[]:t:f;if(g&&g(f,d,n,r),v){i=Ce(d,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(d[u[o]]=!(f[u[o]]=a))}if(e){if(y||p){if(y){i=[],o=d.length;while(o--)(a=d[o])&&i.push(f[o]=a);y(null,d=[],i,r)}o=d.length;while(o--)(a=d[o])&&-1<(i=y?H(e,a):s[o])&&(e[i]=!(t[i]=a))}}else d=Ce(d===t?d.splice(l,d.length):d),y?y(null,t,d,r):O.apply(t,d)})}function Ee(e){for(var i,t,n,r=e.length,o=x.relative[e[0].type],a=o||x.relative[" "],s=o?1:0,u=xe(function(e){return e===i},a,!0),l=xe(function(e){return-1<H(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=x.relative[e[s].type])c=[xe(we(c),t)];else{if((t=x.filter[e[s].type].apply(null,e[s].matches))[A]){for(n=++s;n<r;n++)if(x.relative[e[n].type])break;return Te(1<s&&we(c),1<s&&be(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&be(e))}c.push(t)}return we(c)}return me.prototype=x.filters=x.pseudos,x.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=b[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=x.preFilter;while(a){for(o in n&&!(r=z.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=_.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),x.filter)!(r=Q[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):b(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,b,r,i=[],o=[],a=S[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[A]?i.push(a):o.push(a);(a=S(e,(v=o,m=0<(y=i).length,b=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],d=w,p=e||b&&x.find.TAG("*",i),h=N+=null==d?1:Math.random()||.1,g=p.length;for(i&&(w=t==T||t||i);l!==g&&null!=(o=p[l]);l++){if(b&&o){a=0,t||o.ownerDocument==T||(C(o),n=!E);while(s=v[a++])if(s(o,t||T,n)){r.push(o);break}i&&(N=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=j.call(r));f=Ce(f)}O.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(N=h,w=d),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&x.relative[o[1].type]){if(!(t=(x.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=Q.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],x.relative[s=a.type])break;if((u=x.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&be(o)))return O.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},p.sortStable=A.split("").sort(D).join("")===A,p.detectDuplicates=!!l,C(),p.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(T.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),p.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(I,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(g);E.find=p,E.expr=p.selectors,E.expr[":"]=E.expr.pseudos,E.uniqueSort=E.unique=p.uniqueSort,E.text=p.getText,E.isXMLDoc=p.isXML,E.contains=p.contains,E.escapeSelector=p.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&E(e).is(n))break;r.push(e)}return r},A=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},N=E.expr.match.needsContext;function S(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var k=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return b(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1<i.call(n,e)!==r}):E.filter(n,e,r)}E.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?E.find.matchesSelector(r,e)?[r]:[]:E.find.matches(e,E.grep(t,function(e){return 1===e.nodeType}))},E.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(E(e).filter(function(){for(t=0;t<r;t++)if(E.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)E.find(e,i[t],n);return 1<r?E.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&N.test(e)?E(e):e||[],!1).length}});var L,j=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||L,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:j.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:w,!0)),k.test(r[1])&&E.isPlainObject(t))for(r in t)b(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=w.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this)}).prototype=E.fn,L=E(w);var q=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(E.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&E(e);if(!N.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&E.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?E.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(E(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(E.uniqueSort(E.merge(this.get(),E(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),E.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return A((e.parentNode||{}).firstChild,e)},children:function(e){return A(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(S(e,"template")&&(e=e.content||e),E.merge([],e.childNodes))}},function(r,i){E.fn[r]=function(e,t){var n=E.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=E.filter(t,n)),1<this.length&&(O[r]||E.uniqueSort(n),q.test(r)&&n.reverse()),this.pushStack(n)}});var H=/[^\x20\t\r\n\f]+/g;function I(e){return e}function R(e){throw e}function B(e,t,n,r){var i;try{e&&b(i=e.promise)?i.call(e).done(t).fail(n):e&&b(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}E.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},E.each(e.match(H)||[],function(e,t){n[t]=!0}),n):E.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){E.each(e,function(e,t){b(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==T(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return E.each(arguments,function(e,t){var n;while(-1<(n=E.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<E.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},E.extend({Deferred:function(e){var o=[["notify","progress",E.Callbacks("memory"),E.Callbacks("memory"),2],["resolve","done",E.Callbacks("once memory"),E.Callbacks("once memory"),0,"resolved"],["reject","fail",E.Callbacks("once memory"),E.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return E.Deferred(function(r){E.each(o,function(e,t){var n=b(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&b(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,b(t)?s?t.call(e,l(u,o,I,s),l(u,o,R,s)):(u++,t.call(e,l(u,o,I,s),l(u,o,R,s),l(u,o,I,o.notifyWith))):(a!==I&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){E.Deferred.exceptionHook&&E.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==R&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(E.Deferred.getStackHook&&(t.stackTrace=E.Deferred.getStackHook()),g.setTimeout(t))}}return E.Deferred(function(e){o[0][3].add(l(0,e,b(r)?r:I,e.notifyWith)),o[1][3].add(l(0,e,b(t)?t:I)),o[2][3].add(l(0,e,b(n)?n:R))}).promise()},promise:function(e){return null!=e?E.extend(e,a):a}},s={};return E.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=E.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(B(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||b(i[t]&&i[t].then)))return o.then();while(t--)B(i[t],a(t),o.reject);return o.promise()}});var M=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;E.Deferred.exceptionHook=function(e,t){g.console&&g.console.warn&&e&&M.test(e.name)&&g.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},E.readyException=function(e){g.setTimeout(function(){throw e})};var W=E.Deferred();function F(){w.removeEventListener("DOMContentLoaded",F),g.removeEventListener("load",F),E.ready()}E.fn.ready=function(e){return W.then(e)["catch"](function(e){E.readyException(e)}),this},E.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--E.readyWait:E.isReady)||(E.isReady=!0)!==e&&0<--E.readyWait||W.resolveWith(w,[E])}}),E.ready.then=W.then,"complete"===w.readyState||"loading"!==w.readyState&&!w.documentElement.doScroll?g.setTimeout(E.ready):(w.addEventListener("DOMContentLoaded",F),g.addEventListener("load",F));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===T(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,b(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(E(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},z=/^-ms-/,_=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function V(e){return e.replace(z,"ms-").replace(_,U)}var X=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function Q(){this.expando=E.expando+Q.uid++}Q.uid=1,Q.prototype={cache:function(e){var t=e[this.expando];return t||(t={},X(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[V(t)]=n;else for(r in t)i[V(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][V(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(V):(t=V(t))in r?[t]:t.match(H)||[]).length;while(n--)delete r[t[n]]}(void 0===t||E.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!E.isEmptyObject(t)}};var Y=new Q,G=new Q,K=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,J=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(J,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:K.test(i)?JSON.parse(i):i)}catch(e){}G.set(e,t,n)}else n=void 0;return n}E.extend({hasData:function(e){return G.hasData(e)||Y.hasData(e)},data:function(e,t,n){return G.access(e,t,n)},removeData:function(e,t){G.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),E.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=G.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=V(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){G.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=G.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){G.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){G.remove(this,e)})}}),E.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,E.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=E.queue(e,t),r=n.length,i=n.shift(),o=E._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){E.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:E.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),E.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?E.queue(this[0],t):void 0===n?this:this.each(function(){var e=E.queue(this,t,n);E._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&E.dequeue(this,t)})},dequeue:function(e){return this.each(function(){E.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=E.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=w.documentElement,ie=function(e){return E.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return E.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===E.css(e,"display")};var se={};function ue(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=se[s])||(o=a.body.appendChild(a.createElement(s)),u=E.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),se[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}E.fn.extend({show:function(){return ue(this,!0)},hide:function(){return ue(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?E(this).show():E(this).hide()})}});var le,ce,fe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i;le=w.createDocumentFragment().appendChild(w.createElement("div")),(ce=w.createElement("input")).setAttribute("type","radio"),ce.setAttribute("checked","checked"),ce.setAttribute("name","t"),le.appendChild(ce),m.checkClone=le.cloneNode(!0).cloneNode(!0).lastChild.checked,le.innerHTML="<textarea>x</textarea>",m.noCloneChecked=!!le.cloneNode(!0).lastChild.defaultValue,le.innerHTML="<option></option>",m.option=!!le.lastChild;var he={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?E.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}he.tbody=he.tfoot=he.colgroup=he.caption=he.thead,he.th=he.td,m.option||(he.optgroup=he.option=[1,"<select multiple='multiple'>","</select>"]);var ye=/<|&#?\w+;/;function me(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p<h;p++)if((o=e[p])||0===o)if("object"===T(o))E.merge(d,o.nodeType?[o]:o);else if(ye.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=he[s]||he._default,a.innerHTML=u[1]+E.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;E.merge(d,a.childNodes),(a=f.firstChild).textContent=""}else d.push(t.createTextNode(o));f.textContent="",p=0;while(o=d[p++])if(r&&-1<E.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ge(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])pe.test(o.type||"")&&n.push(o)}return f}var be=/^key/,xe=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,we=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Te(){return!1}function Ee(e,t){return e===function(){try{return w.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Te;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return E().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=E.guid++)),e.each(function(){E.event.add(this,t,i,r,n)})}function Ne(e,i,o){o?(Y.set(e,i,!1),E.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(E.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:E.event.trigger(E.extend(r[0],E.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&E.event.add(e,i,Ce)}E.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,d,p,h,g,v=Y.get(t);if(X(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&E.find.matchesSelector(re,i),n.guid||(n.guid=E.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof E&&E.event.triggered!==e.type?E.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(H)||[""]).length;while(l--)p=g=(s=we.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),p&&(f=E.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=E.event.special[p]||{},c=E.extend({type:p,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&E.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=u[p])||((d=u[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(p,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),E.event.global[p]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,d,p,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(H)||[""]).length;while(l--)if(p=g=(s=we.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),p){f=E.event.special[p]||{},d=u[p=(r?f.delegateType:f.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;while(o--)c=d[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||E.removeEvent(e,p,v.handle),delete u[p])}else for(p in u)E.event.remove(e,p+t[l],n,r,!0);E.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=E.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=E.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=E.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((E.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<E(i,this).index(l):E.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(E.Event.prototype,t,{enumerable:!0,configurable:!0,get:b(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[E.expando]?e:new E.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return fe.test(t.type)&&t.click&&S(t,"input")&&Ne(t,"click",Ce),!1},trigger:function(e){var t=this||e;return fe.test(t.type)&&t.click&&S(t,"input")&&Ne(t,"click"),!0},_default:function(e){var t=e.target;return fe.test(t.type)&&t.click&&S(t,"input")&&Y.get(t,"click")||S(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},E.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},E.Event=function(e,t){if(!(this instanceof E.Event))return new E.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Te,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&E.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[E.expando]=!0},E.Event.prototype={constructor:E.Event,isDefaultPrevented:Te,isPropagationStopped:Te,isImmediatePropagationStopped:Te,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},E.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&xe.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},E.event.addProp),E.each({focus:"focusin",blur:"focusout"},function(e,t){E.event.special[e]={setup:function(){return Ne(this,e,Ee),!1},trigger:function(){return Ne(this,e),!0},delegateType:t}}),E.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){E.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||E.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),E.fn.extend({on:function(e,t,n,r){return Ae(this,e,t,n,r)},one:function(e,t,n,r){return Ae(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,E(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Te),this.each(function(){E.event.remove(this,e,n,t)})}});var Se=/<script|<style|<link/i,ke=/checked\s*(?:[^=]|=\s*.checked.)/i,De=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Le(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function je(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)E.event.add(t,i,s[i][n]);G.hasData(e)&&(o=G.access(e),a=E.extend({},o),G.set(t,a))}}function Pe(n,r,i,o){r=v(r);var e,t,a,s,u,l,c=0,f=n.length,d=f-1,p=r[0],h=b(p);if(h||1<f&&"string"==typeof p&&!m.checkClone&&ke.test(p))return n.each(function(e){var t=n.eq(e);h&&(r[0]=p.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=me(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=E.map(ge(e,"script"),je)).length;c<f;c++)u=e,c!==d&&(u=E.clone(u,!0,!0),s&&E.merge(a,ge(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,E.map(a,qe),c=0;c<s;c++)u=a[c],pe.test(u.type||"")&&!Y.access(u,"globalEval")&&E.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?E._evalUrl&&!u.noModule&&E._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):C(u.textContent.replace(De,""),u,l))}return n}function He(e,t,n){for(var r,i=t?E.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||E.cleanData(ge(r)),r.parentNode&&(n&&ie(r)&&ve(ge(r,"script")),r.parentNode.removeChild(r));return e}E.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(m.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||E.isXMLDoc(e)))for(a=ge(c),r=0,i=(o=ge(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&fe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ge(e),a=a||ge(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ge(c,"script")).length&&ve(a,!f&&ge(e,"script")),c},cleanData:function(e){for(var t,n,r,i=E.event.special,o=0;void 0!==(n=e[o]);o++)if(X(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?E.event.remove(n,r):E.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[G.expando]&&(n[G.expando]=void 0)}}}),E.fn.extend({detach:function(e){return He(this,e,!0)},remove:function(e){return He(this,e)},text:function(e){return $(this,function(e){return void 0===e?E.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(E.cleanData(ge(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return E.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Se.test(e)&&!he[(de.exec(e)||["",""])[1].toLowerCase()]){e=E.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(E.cleanData(ge(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;E.inArray(this,n)<0&&(E.cleanData(ge(this)),t&&t.replaceChild(e,this))},n)}}),E.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){E.fn[e]=function(e){for(var t,n=[],r=E(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),E(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Ie=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Re=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=g),t.getComputedStyle(e)},Be=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Me=new RegExp(ne.join("|"),"i");function We(e,t,n){var r,i,o,a,s=e.style;return(n=n||Re(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=E.style(e,t)),!m.pixelBoxStyles()&&Ie.test(a)&&Me.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function Fe(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=g.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=w.createElement("div"),l=w.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",m.clearCloneStyle="content-box"===l.style.backgroundClip,E.extend(m,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=w.createElement("table"),t=w.createElement("tr"),n=w.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=g.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var $e=["Webkit","Moz","ms"],ze=w.createElement("div").style,_e={};function Ue(e){var t=E.cssProps[e]||_e[e];return t||(e in ze?e:_e[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=$e.length;while(n--)if((e=$e[n]+t)in ze)return e}(e)||e)}var Ve,Xe,Qe=/^(none|table(?!-c[ea]).+)/,Ye=/^--/,Ge={position:"absolute",visibility:"hidden",display:"block"},Ke={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ze(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=E.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=E.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=E.css(e,"border"+ne[a]+"Width",!0,i))):(u+=E.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=E.css(e,"border"+ne[a]+"Width",!0,i):s+=E.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function et(e,t,n){var r=Re(e),i=(!m.boxSizingReliable()||n)&&"border-box"===E.css(e,"boxSizing",!1,r),o=i,a=We(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Ie.test(a)){if(!n)return a;a="auto"}return(!m.boxSizingReliable()&&i||!m.reliableTrDimensions()&&S(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===E.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===E.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ze(e,t,n||(i?"border":"content"),o,r,a)+"px"}E.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=We(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=V(t),u=Ye.test(t),l=e.style;if(u||(t=Ue(s)),a=E.cssHooks[t]||E.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=function(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return E.css(e,t,"")},u=s(),l=n&&n[3]||(E.cssNumber[t]?"":"px"),c=e.nodeType&&(E.cssNumber[t]||"px"!==l&&+u)&&te.exec(E.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)E.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,E.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(E.cssNumber[s]?"":"px")),m.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=V(t);return Ye.test(t)||(t=Ue(s)),(a=E.cssHooks[t]||E.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=We(e,t,r)),"normal"===i&&t in Ke&&(i=Ke[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),E.each(["height","width"],function(e,u){E.cssHooks[u]={get:function(e,t,n){if(t)return!Qe.test(E.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,u,n):Be(e,Ge,function(){return et(e,u,n)})},set:function(e,t,n){var r,i=Re(e),o=!m.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===E.css(e,"boxSizing",!1,i),s=n?Ze(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ze(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=E.css(e,u)),Je(0,t,s)}}}),E.cssHooks.marginLeft=Fe(m.reliableMarginLeft,function(e,t){if(t)return(parseFloat(We(e,"marginLeft"))||e.getBoundingClientRect().left-Be(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),E.each({margin:"",padding:"",border:"Width"},function(i,o){E.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(E.cssHooks[i+o].set=Je)}),E.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Re(e),i=t.length;a<i;a++)o[t[a]]=E.css(e,t[a],!1,r);return o}return void 0!==n?E.style(e,t,n):E.css(e,t)},e,t,1<arguments.length)}}),E.fn.delay=function(r,e){return r=E.fx&&E.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=g.setTimeout(e,r);t.stop=function(){g.clearTimeout(n)}})},Ve=w.createElement("input"),Xe=w.createElement("select").appendChild(w.createElement("option")),Ve.type="checkbox",m.checkOn=""!==Ve.value,m.optSelected=Xe.selected,(Ve=w.createElement("input")).value="t",Ve.type="radio",m.radioValue="t"===Ve.value;var tt,nt=E.expr.attrHandle;E.fn.extend({attr:function(e,t){return $(this,E.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){E.removeAttr(this,e)})}}),E.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?E.prop(e,t,n):(1===o&&E.isXMLDoc(e)||(i=E.attrHooks[t.toLowerCase()]||(E.expr.match.bool.test(t)?tt:void 0)),void 0!==n?null===n?void E.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=E.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!m.radioValue&&"radio"===t&&S(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(H);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),tt={set:function(e,t,n){return!1===t?E.removeAttr(e,n):e.setAttribute(n,n),n}},E.each(E.expr.match.bool.source.match(/\w+/g),function(e,t){var a=nt[t]||E.find.attr;nt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=nt[o],nt[o]=r,r=null!=a(e,t,n)?o:null,nt[o]=i),r}});var rt=/^(?:input|select|textarea|button)$/i,it=/^(?:a|area)$/i;function ot(e){return(e.match(H)||[]).join(" ")}function at(e){return e.getAttribute&&e.getAttribute("class")||""}function st(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(H)||[]}E.fn.extend({prop:function(e,t){return $(this,E.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[E.propFix[e]||e]})}}),E.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&E.isXMLDoc(e)||(t=E.propFix[t]||t,i=E.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=E.find.attr(e,"tabindex");return t?parseInt(t,10):rt.test(e.nodeName)||it.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),m.optSelected||(E.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),E.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){E.propFix[this.toLowerCase()]=this}),E.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(b(t))return this.each(function(e){E(this).addClass(t.call(this,e,at(this)))});if((e=st(t)).length)while(n=this[u++])if(i=at(n),r=1===n.nodeType&&" "+ot(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=ot(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(b(t))return this.each(function(e){E(this).removeClass(t.call(this,e,at(this)))});if(!arguments.length)return this.attr("class","");if((e=st(t)).length)while(n=this[u++])if(i=at(n),r=1===n.nodeType&&" "+ot(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=ot(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):b(i)?this.each(function(e){E(this).toggleClass(i.call(this,e,at(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=E(this),r=st(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=at(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+ot(at(n))+" ").indexOf(t))return!0;return!1}});var ut=/\r/g;E.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=b(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,E(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=E.map(t,function(e){return null==e?"":e+""})),(r=E.valHooks[this.type]||E.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=E.valHooks[t.type]||E.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(ut,""):null==e?"":e:void 0}}),E.extend({valHooks:{option:{get:function(e){var t=E.find.attr(e,"value");return null!=t?t:ot(E.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!S(n.parentNode,"optgroup"))){if(t=E(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=E.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<E.inArray(E.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),E.each(["radio","checkbox"],function(){E.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<E.inArray(E(e).val(),t)}},m.checkOn||(E.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),m.focusin="onfocusin"in g;var lt=/^(?:focusinfocus|focusoutblur)$/,ct=function(e){e.stopPropagation()};E.extend(E.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,d=[n||w],p=y.call(e,"type")?e.type:e,h=y.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||w,3!==n.nodeType&&8!==n.nodeType&&!lt.test(p+E.event.triggered)&&(-1<p.indexOf(".")&&(p=(h=p.split(".")).shift(),h.sort()),u=p.indexOf(":")<0&&"on"+p,(e=e[E.expando]?e:new E.Event(p,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:E.makeArray(t,[e]),c=E.event.special[p]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||p,lt.test(s+p)||(o=o.parentNode);o;o=o.parentNode)d.push(o),a=o;a===(n.ownerDocument||w)&&d.push(a.defaultView||a.parentWindow||g)}i=0;while((o=d[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||p,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&X(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=p,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(d.pop(),t)||!X(n)||u&&b(n[p])&&!x(n)&&((a=n[u])&&(n[u]=null),E.event.triggered=p,e.isPropagationStopped()&&f.addEventListener(p,ct),n[p](),e.isPropagationStopped()&&f.removeEventListener(p,ct),E.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=E.extend(new E.Event,n,{type:e,isSimulated:!0});E.event.trigger(r,null,t)}}),E.fn.extend({trigger:function(e,t){return this.each(function(){E.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return E.event.trigger(e,t,n,!0)}}),m.focusin||E.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){E.event.simulate(r,e.target,E.event.fix(e))};E.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}}),E.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new g.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||E.error("Invalid XML: "+e),t};var ft,dt=/\[\]$/,pt=/\r?\n/g,ht=/^(?:submit|button|image|reset|file)$/i,gt=/^(?:input|select|textarea|keygen)/i;function vt(n,e,r,i){var t;if(Array.isArray(e))E.each(e,function(e,t){r||dt.test(n)?i(n,t):vt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==T(e))i(n,e);else for(t in e)vt(n+"["+t+"]",e[t],r,i)}E.param=function(e,t){var n,r=[],i=function(e,t){var n=b(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!E.isPlainObject(e))E.each(e,function(){i(this.name,this.value)});else for(n in e)vt(n,e[n],t,i);return r.join("&")},E.fn.extend({serialize:function(){return E.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=E.prop(this,"elements");return e?E.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!E(this).is(":disabled")&>.test(this.nodeName)&&!ht.test(e)&&(this.checked||!fe.test(e))}).map(function(e,t){var n=E(this).val();return null==n?null:Array.isArray(n)?E.map(n,function(e){return{name:t.name,value:e.replace(pt,"\r\n")}}):{name:t.name,value:n.replace(pt,"\r\n")}}).get()}}),E.fn.extend({wrapAll:function(e){var t;return this[0]&&(b(e)&&(e=e.call(this[0])),t=E(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return b(n)?this.each(function(e){E(this).wrapInner(n.call(this,e))}):this.each(function(){var e=E(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=b(t);return this.each(function(e){E(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){E(this).replaceWith(this.childNodes)}),this}}),E.expr.pseudos.hidden=function(e){return!E.expr.pseudos.visible(e)},E.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},m.createHTMLDocument=((ft=w.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===ft.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(m.createHTMLDocument?((r=(t=w.implementation.createHTMLDocument("")).createElement("base")).href=w.location.href,t.head.appendChild(r)):t=w),o=!n&&[],(i=k.exec(e))?[t.createElement(i[1])]:(i=me([e],t,o),o&&o.length&&E(o).remove(),E.merge([],i.childNodes)));var r,i,o},E.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=E.css(e,"position"),c=E(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=E.css(e,"top"),u=E.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),b(t)&&(t=t.call(e,n,E.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),i.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-E.css(r,"marginTop",!0),left:t.left-i.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===E.css(e,"position"))e=e.offsetParent;return e||re})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;E.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=Fe(m.pixelPosition,function(e,t){if(t)return t=We(e,n),Ie.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){E.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,i):E.style(e,t,n,i)},s,n?e:void 0,n)}})}),E.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var yt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;E.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),b(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||E.guid++,i},E.holdReady=function(e){e?E.readyWait++:E.ready(!0)},E.isArray=Array.isArray,E.parseJSON=JSON.parse,E.nodeName=S,E.isFunction=b,E.isWindow=x,E.camelCase=V,E.type=T,E.now=Date.now,E.isNumeric=function(e){var t=E.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},E.trim=function(e){return null==e?"":(e+"").replace(yt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return E});var mt=g.jQuery,bt=g.$;return E.noConflict=function(e){return g.$===E&&(g.$=bt),e&&g.jQuery===E&&(g.jQuery=mt),E},"undefined"==typeof e&&(g.jQuery=g.$=E),E}); diff --git a/apps/auth/src/assets/js/vendors/popper.min.js b/apps/auth/src/assets/js/vendors/popper.min.js new file mode 100644 index 000000000..bb1aaae3e --- /dev/null +++ b/apps/auth/src/assets/js/vendors/popper.min.js @@ -0,0 +1,5 @@ +/* + Copyright (C) Federico Zivolo 2020 + Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). + */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',n=e.nodeName;if('BODY'===n||'HTML'===n){var i=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||i;return r[o]}return e[o]}function f(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],n=l(t,'top'),i=l(t,'left'),r=o?-1:1;return e.top+=n*r,e.bottom+=n*r,e.left+=i*r,e.right+=i*r,e}function m(e,t){var o='x'===t?'Left':'Top',n='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'])+parseFloat(e['border'+n+'Width'])}function h(e,t,o,n){return ee(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],r(10)?parseInt(o['offset'+e])+parseInt(n['margin'+('Height'===e?'Top':'Left')])+parseInt(n['margin'+('Height'===e?'Bottom':'Right')]):0)}function c(e){var t=e.body,o=e.documentElement,n=r(10)&&getComputedStyle(o);return{height:h('Height',t,o,n),width:h('Width',t,o,n)}}function g(e){return le({},e,{right:e.left+e.width,bottom:e.top+e.height})}function u(e){var o={};try{if(r(10)){o=e.getBoundingClientRect();var n=l(e,'top'),i=l(e,'left');o.top+=n,o.left+=i,o.bottom+=n,o.right+=i}else o=e.getBoundingClientRect()}catch(t){}var p={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},s='HTML'===e.nodeName?c(e.ownerDocument):{},d=s.width||e.clientWidth||p.width,a=s.height||e.clientHeight||p.height,f=e.offsetWidth-d,h=e.offsetHeight-a;if(f||h){var u=t(e);f-=m(u,'x'),h-=m(u,'y'),p.width-=f,p.height-=h}return g(p)}function b(e,o){var i=2<arguments.length&&void 0!==arguments[2]&&arguments[2],p=r(10),s='HTML'===o.nodeName,d=u(e),a=u(o),l=n(e),m=t(o),h=parseFloat(m.borderTopWidth),c=parseFloat(m.borderLeftWidth);i&&s&&(a.top=ee(a.top,0),a.left=ee(a.left,0));var b=g({top:d.top-a.top-h,left:d.left-a.left-c,width:d.width,height:d.height});if(b.marginTop=0,b.marginLeft=0,!p&&s){var w=parseFloat(m.marginTop),y=parseFloat(m.marginLeft);b.top-=h-w,b.bottom-=h-w,b.left-=c-y,b.right-=c-y,b.marginTop=w,b.marginLeft=y}return(p&&!i?o.contains(l):o===l&&'BODY'!==l.nodeName)&&(b=f(b,o)),b}function w(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=e.ownerDocument.documentElement,n=b(e,o),i=ee(o.clientWidth,window.innerWidth||0),r=ee(o.clientHeight,window.innerHeight||0),p=t?0:l(o),s=t?0:l(o,'left'),d={top:p-n.top+n.marginTop,left:s-n.left+n.marginLeft,width:i,height:r};return g(d)}function y(e){var n=e.nodeName;if('BODY'===n||'HTML'===n)return!1;if('fixed'===t(e,'position'))return!0;var i=o(e);return!!i&&y(i)}function E(e){if(!e||!e.parentElement||r())return document.documentElement;for(var o=e.parentElement;o&&'none'===t(o,'transform');)o=o.parentElement;return o||document.documentElement}function v(e,t,r,p){var s=4<arguments.length&&void 0!==arguments[4]&&arguments[4],d={top:0,left:0},l=s?E(e):a(e,i(t));if('viewport'===p)d=w(l,s);else{var f;'scrollParent'===p?(f=n(o(t)),'BODY'===f.nodeName&&(f=e.ownerDocument.documentElement)):'window'===p?f=e.ownerDocument.documentElement:f=p;var m=b(f,l,s);if('HTML'===f.nodeName&&!y(l)){var h=c(e.ownerDocument),g=h.height,u=h.width;d.top+=m.top-m.marginTop,d.bottom=g+m.top,d.left+=m.left-m.marginLeft,d.right=u+m.left}else d=m}r=r||0;var v='number'==typeof r;return d.left+=v?r:r.left||0,d.top+=v?r:r.top||0,d.right-=v?r:r.right||0,d.bottom-=v?r:r.bottom||0,d}function x(e){var t=e.width,o=e.height;return t*o}function O(e,t,o,n,i){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=v(o,n,r,i),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return le({key:e},s[e],{area:x(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,n=e.height;return t>=o.clientWidth&&n>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function L(e,t,o){var n=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,r=n?E(t):a(t,i(o));return b(o,r,n)}function S(e){var t=e.ownerDocument.defaultView,o=t.getComputedStyle(e),n=parseFloat(o.marginTop||0)+parseFloat(o.marginBottom||0),i=parseFloat(o.marginLeft||0)+parseFloat(o.marginRight||0),r={width:e.offsetWidth+i,height:e.offsetHeight+n};return r}function T(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function C(e,t,o){o=o.split('-')[0];var n=S(e),i={width:n.width,height:n.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return i[p]=t[p]+t[d]/2-n[d]/2,i[s]=o===s?t[s]-n[a]:t[T(s)],i}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function N(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var n=D(e,function(e){return e[t]===o});return e.indexOf(n)}function P(t,o,n){var i=void 0===n?t:t.slice(0,N(t,'name',n));return i.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var n=t['function']||t.fn;t.enabled&&e(n)&&(o.offsets.popper=g(o.offsets.popper),o.offsets.reference=g(o.offsets.reference),o=n(o,t))}),o}function k(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=O(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=C(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?'fixed':'absolute',e=P(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var o=e.name,n=e.enabled;return n&&o===t})}function B(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function H(){return this.state.isDestroyed=!0,W(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.position='',this.popper.style.top='',this.popper.style.left='',this.popper.style.right='',this.popper.style.bottom='',this.popper.style.willChange='',this.popper.style[B('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function A(e){var t=e.ownerDocument;return t?t.defaultView:window}function M(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||M(n(p.parentNode),t,o,i),i.push(p)}function F(e,t,o,i){o.updateBound=i,A(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return M(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function I(){this.state.eventsEnabled||(this.state=F(this.reference,this.options,this.state,this.scheduleUpdate))}function R(e,t){return A(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function U(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=R(this.reference,this.state))}function Y(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function V(e,t){Object.keys(t).forEach(function(o){var n='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&Y(t[o])&&(n='px'),e.style[o]=t[o]+n})}function j(e,t){Object.keys(t).forEach(function(o){var n=t[o];!1===n?e.removeAttribute(o):e.setAttribute(o,t[o])})}function q(e,t){var o=e.offsets,n=o.popper,i=o.reference,r=$,p=function(e){return e},s=r(i.width),d=r(n.width),a=-1!==['left','right'].indexOf(e.placement),l=-1!==e.placement.indexOf('-'),f=t?a||l||s%2==d%2?r:Z:p,m=t?r:p;return{left:f(1==s%2&&1==d%2&&!l&&t?n.left-1:n.left),top:m(n.top),bottom:m(n.bottom),right:f(n.right)}}function K(e,t,o){var n=D(e,function(e){var o=e.name;return o===t}),i=!!n&&e.some(function(e){return e.name===o&&e.enabled&&e.order<n.order});if(!i){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return i}function z(e){return'end'===e?'start':'start'===e?'end':e}function G(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=he.indexOf(e),n=he.slice(o+1).concat(he.slice(0,o));return t?n.reverse():n}function _(e,t,o,n){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+i[1],p=i[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=n;}var d=g(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?ee(document.documentElement.clientHeight,window.innerHeight||0):ee(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function X(e,t,o,n){var i=[0,0],r=-1!==['right','left'].indexOf(n),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(D(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,n){var i=(1===n?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return _(e,i,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,n){Y(o)&&(i[t]+=o*('-'===e[n-1]?-1:1))})}),i}function J(e,t){var o,n=t.offset,i=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=i.split('-')[0];return o=Y(+n)?[+n,0]:X(n,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}var Q=Math.min,Z=Math.floor,$=Math.round,ee=Math.max,te='undefined'!=typeof window&&'undefined'!=typeof document&&'undefined'!=typeof navigator,oe=function(){for(var e=['Edge','Trident','Firefox'],t=0;t<e.length;t+=1)if(te&&0<=navigator.userAgent.indexOf(e[t]))return 1;return 0}(),ne=te&&window.Promise,ie=ne?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},oe))}},re=te&&!!(window.MSInputMethodContext&&document.documentMode),pe=te&&/MSIE 10/.test(navigator.userAgent),se=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},de=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,n){return o&&e(t.prototype,o),n&&e(t,n),t}}(),ae=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},le=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var n in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},fe=te&&/Firefox/i.test(navigator.userAgent),me=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],he=me.slice(3),ce={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},ge=function(){function t(o,n){var i=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};se(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=ie(this.update.bind(this)),this.options=le({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(le({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=le({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return le({name:e},i.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return de(t,[{key:'update',value:function(){return k.call(this)}},{key:'destroy',value:function(){return H.call(this)}},{key:'enableEventListeners',value:function(){return I.call(this)}},{key:'disableEventListeners',value:function(){return U.call(this)}}]),t}();return ge.Utils=('undefined'==typeof window?global:window).PopperUtils,ge.placements=me,ge.Defaults={placement:'bottom',positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],n=t.split('-')[1];if(n){var i=e.offsets,r=i.reference,p=i.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:ae({},d,r[d]),end:ae({},d,r[d]+r[a]-p[a])};e.offsets.popper=le({},p,l[n])}return e}},offset:{order:200,enabled:!0,fn:J,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||p(e.instance.popper);e.instance.reference===o&&(o=p(o));var n=B('transform'),i=e.instance.popper.style,r=i.top,s=i.left,d=i[n];i.top='',i.left='',i[n]='';var a=v(e.instance.popper,e.instance.reference,t.padding,o,e.positionFixed);i.top=r,i.left=s,i[n]=d,t.boundaries=a;var l=t.priority,f=e.offsets.popper,m={primary:function(e){var o=f[e];return f[e]<a[e]&&!t.escapeWithReference&&(o=ee(f[e],a[e])),ae({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=f[o];return f[e]>a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(n[d])&&(e.offsets.popper[d]=r(n[d])-o[a]),o[d]>r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[c]-u)),d[m]+u>s[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)<f(l.right)||'top'===n&&f(a.bottom)>f(l.top)||'bottom'===n&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,n=t.y,i=e.offsets.popper,r=D(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==r&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===r?t.gpuAcceleration:r,l=p(e.instance.popper),f=u(l),m={position:i.position},h=q(e,2>window.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); +//# sourceMappingURL=popper.min.js.map diff --git a/apps/auth/src/assets/site.webmanifest b/apps/auth/src/assets/site.webmanifest new file mode 100644 index 000000000..af658222d --- /dev/null +++ b/apps/auth/src/assets/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "THX", + "short_name": "THX", + "icons": [ + { + "src": "/img/icons/android-chrome-192x192.png?v=pgdmXmoa2w", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/img/icons/android-chrome-512x512.png?v=pgdmXmoa2w", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/apps/auth/src/assets/views/account.ejs b/apps/auth/src/assets/views/account.ejs new file mode 100644 index 000000000..7efbecf08 --- /dev/null +++ b/apps/auth/src/assets/views/account.ejs @@ -0,0 +1,255 @@ +<div class="col-md-6 offset-md-3 order-0 order-sm-1"> + <div class="text-center pb-4 pt-4"> + <img src="/img/logo.png" width="60" alt="THX Logo" /> + </div> + <div class="card shadow-sm mb-5 w-100"> + <div class="card-body p-sm-5"> + <strong class="h3 d-block text-dark text-center mb-5"> + Account + </strong> + <% if (alert && alert.message) { %> + <div class="alert alert-<%= alert.variant %>"> + <%= alert.message %> + </div> + <% } %> + + <form id="account_update" autocomplete="off" action="/oidc/<%= uid %>/account" enctype="multipart/form-data" + method="POST"> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center"> + Picture + </div> + <div class="col-8"> + <div class="input-group"> + <% if (params.profileImg) { %> + <div class="input-group-prepend px-0"> + <div class="input-group-text"> + <img width="25" src="<%= params.profileImg %>" alt="profle-picture" + class="rounded-circle" /> + </div> + </div> + <% } %> + <input class="form-control" name="profile" type="file" accept="image/*"> + </div> + </div> + </div> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center"> + Email + </div> + <div class="col-8"> + <div class="input-group"> + <div class="input-group-prepend"> + <span class="input-group-text px-3"> + <% if (params.isEmailVerified) { %> + <i class="fas fa-check-circle text-success"></i> + <% } else { %> + <i class="fas fa-check-circle text-danger"></i> + <% }%> + </span> + </div> + <% if (!params.email) { %> + <input class="form-control" name="email" required value="" /> + <% } else if (!params.isEmailVerified) { %> + <input class="form-control" name="email" required value="<%= params.email %>" /> + <% } else { %> + <input class="form-control" name="email" required disabled value="<%= params.email %>" /> + <% } %> + </div> + </div> + </div> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center"> + Plan + <sup data-toggle="tooltip" + title="Send an e-mail to info@thx.network if you want to upgrade or cancel your current plan."> + <i class="fas fa-question-circle text-gray ml-1"></i> + </sup> + </div> + <div class="col-8"> + <input type="text" class="form-control" disabled value="<%= params.planType %>" /> + </div> + </div> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center"> + First name + </div> + <div class="col-8"> + <input class="form-control" name="firstName" value="<%= params.firstName %>" placeholder="" /> + </div> + </div> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center"> + Last name + </div> + <div class="col-8"> + <input class="form-control" name="lastName" value="<%= params.lastName %>" placeholder="" /> + </div> + </div> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center "> + Organisation + </div> + <div class="col-8"> + <input class="form-control" name="organisation" value="<%= params.organisation %>" + placeholder="" /> + </div> + </div> + + <div class="row pb-3"> + <div class="d-flex col-4 align-items-center"> + Website + </div> + <div class="col-8"> + <input class="form-control" name="website" value="<%= params.website %>" placeholder="" /> + </div> + </div> + + <% if (params.googleAccess) { %> + <input form="account_update" type="hidden" name="googleAccess" value="false" /> + <% } %> + + <% if (params.twitterAccess) { %> + <input form="account_update" type="hidden" name="twitterAccess" value="false" /> + <% } %> + + <input type="hidden" name="return_url" value="<%= params.return_url %>" /> + </form> + <hr> + <div class="row"> + <div class="d-flex col-4 align-items-start flex-column font-weight-bold"> + Connect + <small class="text-muted">Link your other accounts using single sign-on.</small> + </div> + <div class="d-flex col-8"> + <div class="w-100 d-flex flex-column"> + <!-- <% if (params.twitchLoginUrl) { %> + <a class="btn btn-sign-in btn-twitch btn-block my-1" href="<%= params.twitchLoginUrl %>"> + <div class="logo"> + <img src="/img/twitch-logo.png" alt="twitch logo" /> + </div> + <div class="label">Connect Twitch</div> + </a> + <% } %> + <% if (!params.twitchLoginUrl) { %> + <form method="post" action="/oidc/<%= uid %>/tokens/twitch/disconnect"> + <button class="btn btn-sign-in btn-discord bg-gray btn-block my-1" type="submit"> + <div class="logo"> + <img src="/img/twitch-logo.png" alt="Twitch logo" /> + </div> + <div class="label">Disconnect Twitch</div> + </button> + </form> + <% } %> --> + <% if (params.discordLoginUrl) { %> + <a class="btn btn-sign-in btn-discord btn-block my-1" href="<%= params.discordLoginUrl %>"> + <div class="logo"> + <img src="/img/discord-logo.png" alt="Discord logo" /> + </div> + <div class="label">Connect Discord</div> + </a> + <% } %> + <% if (!params.discordLoginUrl) { %> + <form method="post" action="/oidc/<%= uid %>/tokens/discord/disconnect"> + <button class="btn btn-sign-in btn-twitch bg-gray btn-block my-1" type="submit"> + <div class="logo"> + <img src="/img/discord-logo.png" alt="Twitch logo" /> + </div> + <div class="label">Disconnect Discord</div> + </button> + </form> + <% } %> + <!-- <% if (!params.googleLoginUrl) { %> + <a class="btn btn-sign-in btn-google btn-block my-1" href="<%= params.googleLoginUrl %>"> + <div class="logo"> + <img src="/img/g-logo.png" alt="Google logo" /> + </div> + <div class="label">Connect Google</div> + </a> + <% } %> + <% if (params.googleLoginUrl) { %> + <form method="post" action="/oidc/<%= uid %>/tokens/google/disconnect"> + <button class="btn btn-sign-in btn-google bg-gray btn-block my-1" type="submit"> + <div class="logo"> + <img src="/img/g-logo.png" alt="Google logo" /> + </div> + <div class="label">Disconnect Google</div> + </button> + </form> + <% } %> + <% if (params.githubLoginUrl) { %> + <a class="btn btn-sign-in btn-github btn-block my-1" href="<%= params.githubLoginUrl %>"> + <div class="logo"> + <img src="/img/github-logo.png" alt="github logo" /> + </div> + <div class="label">Connect Github</div> + </a> + <% } %> + <% if (!params.githubLoginUrl) { %> + <form method="post" action="/oidc/<%= uid %>/tokens/github/disconnect"> + <button class="btn btn-sign-in bg-gray btn-github btn-block my-1" type="submit"> + <div class="logo"> + <img src="/img/github-logo.png" alt="github logo" /> + </div> + <div class="label">Disconnect Github</div> + </button> + </form> + <% } %> --> + <% if (params.twitterLoginUrl) { %> + <a class="btn btn-sign-in btn-twitter btn-block my-1" href="<%= params.twitterLoginUrl %>"> + <div class="logo"> + <img src="/img/t-logo.png" alt="Twitter logo" /> + </div> + <div class="label">Connect Twitter</div> + </a> + <% } %> + <% if (!params.twitterLoginUrl) { %> + <form method="post" action="/oidc/<%= uid %>/tokens/twitter/disconnect"> + <button class="btn btn-sign-in bg-gray btn-twitter btn-block my-1" type="submit"> + <div class="logo"> + <img src="/img/t-logo.png" alt="Twitter logo" /> + </div> + <div class="label">Disconnect Twitter</div> + </button> + </form> + <% } %> + </div> + </div> + </div> + <hr> + <div class="row pb-3"> + <div class="d-flex col-4 align-items-start justify-content-start flex-column font-weight-bold"> + MFA + <small class="text-muted">Multi-factor Authentication using time-based one-time passwords + (TOTP).</small> + </div> + <div class="col-8"> + <% if (params.otpSecret) { %> + <form autocomplete="off" action="/oidc/<%= uid %>/account/totp" method="POST"> + <button class="btn btn-light text-danger btn-block">Deactivate</button> + <input type="hidden" name="disable" value="true" /> + </form> + <% } %> + <% if (!params.otpSecret) { %> + <form autocomplete="off" action="/oidc/<%= uid %>/account/totp" method="POST"> + <button type="submit" class="btn btn-light text-primary btn-block">Activate</button> + </form> + <% } %> + </div> + </div> + + <button class="mt-5 btn btn-primary btn-block rounded-pill" type="submit" form="account_update"> + Update Infomation + </button> + <a class="btn btn-link btn-block rounded-pill" href="<%= params.return_url %>"> + Return to application + </a> + </div> + </div> +</div> \ No newline at end of file diff --git a/apps/auth/src/assets/views/confirm.ejs b/apps/auth/src/assets/views/confirm.ejs new file mode 100644 index 000000000..9ac8bde61 --- /dev/null +++ b/apps/auth/src/assets/views/confirm.ejs @@ -0,0 +1,29 @@ +<div class="col-md-6 offset-md-3 col-xl-4 offset-xl-4 order-0 order-sm-1"> + <div class="text-center pb-4 pt-4"> + <img src="/img/logo.png" width="60" alt="THX Logo" /> + </div> + <div class="card shadow-sm mb-5"> + <div class="card-body p-sm-5"> + <% if (locals.alert && alert.variant === 'danger') { %> + <strong class="h2 d-block text-dark text-center mb-4"> + Oops... + </strong> + <div class="alert alert-<%= alert.variant %>"> + <%= alert.message %> + </div> + <% } else { %> + <strong class="h2 d-block text-dark text-center mb-4"> + Congratulations! + </strong> + <div class="alert alert-success">Your e-mail address has been verified.</div> + <p> + Make sure to enable MFA for your account after signing in. This will significantly improve the security + of your wallet. + </p> + <a href="<%= params.return_url %>" class="btn rounded-pill btn-primary btn-block"> + Sign in + </a> + <% } %> + </div> + </div> +</div> \ No newline at end of file diff --git a/apps/auth/src/assets/views/error.ejs b/apps/auth/src/assets/views/error.ejs new file mode 100644 index 000000000..2a2bac873 --- /dev/null +++ b/apps/auth/src/assets/views/error.ejs @@ -0,0 +1,55 @@ +<div class="col-md-8 offset-md-2 col-xl-4 offset-xl-4 order-0 order-sm-1"> + <div class="text-center pb-4 pt-4"> + <img src="/img/logo.png" width="60" alt="THX Logo" /> + </div> + <div class="card shadow-sm mb-5"> + <div class="card-body pt-sm-5 px-sm-5 pb-md-4"> + <strong class="h3 d-block text-dark mb-4"> + Oops, a wild issue appeared! + </strong> + + <% if (locals.alert && alert.message) { %> + <div class="alert alert-<%= alert.variant %> mt-4 py-1 px-2"> + <i class="fas fa-exclamation-circle mr-1"></i> + <%= alert.message %> + </div> + <% } %> + + <p>Please use the buttons below to find your way back.</p> + <a class="btn rounded-pill btn-light btn-sm btn-block" href="<%= widgetUrl %>" target="_blank"> + Campaigns + <i class="fas fa-chevron-right" aria-hidden="true"></i> + </a> + <a class="btn rounded-pill btn-light btn-sm btn-block" href="<%= publicUrl %>" target="_blank"> + Website + <i class="fas fa-chevron-right" aria-hidden="true"></i> + </a> + <a class="btn rounded-pill btn-light btn-sm btn-block" href="https://docs.thx.network" target="_blank"> + Docs + <i class="fas fa-chevron-right" aria-hidden="true"></i> + </a> + + <% if (returnUrl) { %> + <input type="hidden" name="returnUrl" value="<%= returnUrl %>" /> + <% } %> + </div> + + <div class="card-footer d-flex align-items-center justify-content-end"> + <a id="btnReturn" href="<%= returnUrl %>" class="text-muted mr-auto cursor-pointer">Return to app</a> + <a href="https://discord.com/invite/TzbbSmkE7Y" class="text-muted">Help</a> + </div> + + </div> +</div> +<script> + const returnUrl = document.getElementsByName('returnUrl')[0]; + document.getElementById('btnReturn').addEventListener('click', () => { + if (window.matchMedia('(pointer:coarse)').matches) { + if (returnUrl) { + window.open(returnUrl.value, '_self'); + } + } else { + window.close(); + } + }) +</script> \ No newline at end of file diff --git a/apps/auth/src/assets/views/layouts/default.ejs b/apps/auth/src/assets/views/layouts/default.ejs new file mode 100644 index 000000000..2b61df4fe --- /dev/null +++ b/apps/auth/src/assets/views/layouts/default.ejs @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html lang="en" class="h-100"> + +<head> + <meta charset="UTF-8"> + <title>THX Network | Quest & Reward Engine</title> + <meta name="viewport" + content="width=device-width,initial-scale=1,shrink-to-fit=no,user-scalable=no,maximum-scale=1"> + <meta name="msapplication-TileColor" content="#da532c"> + <meta name="theme-color" content="#ffffff"> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <link rel="stylesheet" href="/css/bootstrap.min.css"> + <link rel="stylesheet" href="/css/main.css"> + <link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png?v=pgdmXmoa2w"> + <link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png?v=pgdmXmoa2w"> + <link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png?v=pgdmXmoa2w"> + <link rel="manifest" href="/site.webmanifest?v=pgdmXmoa2w"> + <link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg?v=pgdmXmoa2w" color="#5bbad5"> +</head> + +<body class="d-lg-flex align-items-center justify-content-center"> + <div class="container"> + <div class="row pt-sm-3 pb-sm-3"> + <%- body %> + </div> + + <% if (gtm) { %> + <script> + (function (w, d, s, l, i) { + w[l] = w[l] || []; + w[l].push({ + 'gtm.start': new Date().getTime(), + 'event': 'gtm.js', + }); + const f = d.getElementsByTagName(s)[0], + j = d.createElement(s), + dl = l != 'dataLayer' ? '&l=' + l : ''; + j.async = true; + j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; + f.parentNode.insertBefore(j, f); + })(window, document, 'script', 'dataLayer', "<%= gtm %>"); + </script> + <% } %> + <script src="/js/vendors/jquery.slim.min.js"></script> + <script src="/js/vendors/bootstrap.bundle.min.js"></script> + <script src="/js/vendors/popper.min.js"></script> + <script> + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="popover"]').popover(); + $('.collapse').collapse({ + toggle: false + }); + }) + </script> + <script src="/js/vendors/fontawesome.06b7267748.js" crossorigin="anonymous"></script> +</body> + +</html> \ No newline at end of file diff --git a/apps/auth/src/assets/views/mail/email-otp.ejs b/apps/auth/src/assets/views/mail/email-otp.ejs new file mode 100644 index 000000000..928100933 --- /dev/null +++ b/apps/auth/src/assets/views/mail/email-otp.ejs @@ -0,0 +1,409 @@ +<!DOCTYPE html> +<html> + +<head> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title>Activate your THX Account</title> + <style> + /* ------------------------------------- + GLOBAL RESETS + ------------------------------------- */ + + img { + border: none; + -ms-interpolation-mode: bicubic; + max-width: 100%; + } + + body { + background-color: #f6f6f6; + font-family: sans-serif; + -webkit-font-smoothing: antialiased; + font-size: 14px; + line-height: 1.4; + margin: 0; + padding: 0; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + } + + table { + border-collapse: separate; + mso-table-lspace: 0pt; + mso-table-rspace: 0pt; + width: 100%; + } + + table td { + font-family: sans-serif; + font-size: 14px; + vertical-align: top; + } + + /* ------------------------------------- + BODY & CONTAINER + ------------------------------------- */ + + .body { + background-color: #f6f6f6; + width: 100%; + } + + /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ + .container { + display: block; + margin: 0 auto !important; + /* makes it centered */ + max-width: 580px; + padding: 10px; + width: 580px; + } + + /* This should also be a block element, so that it will fill 100% of the .container */ + .content { + box-sizing: border-box; + display: block; + margin: 0 auto; + max-width: 580px; + padding: 10px; + } + + /* ------------------------------------- + HEADER, FOOTER, MAIN + ------------------------------------- */ + .main { + background: #ffffff; + border-radius: 3px; + width: 100%; + } + + .wrapper { + box-sizing: border-box; + padding: 20px; + } + + .content-block { + padding-bottom: 10px; + padding-top: 10px; + } + + .footer { + clear: both; + margin-top: 10px; + text-align: center; + width: 100%; + } + + .footer td, + .footer p, + .footer span, + .footer a { + color: #999999; + font-size: 12px; + text-align: center; + } + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1, + h2, + h3, + h4 { + color: #000000; + font-family: sans-serif; + font-weight: 400; + line-height: 1.4; + margin: 0; + margin-bottom: 30px; + } + + h1 { + font-size: 35px; + font-weight: 300; + text-align: center; + text-transform: capitalize; + } + + p, + ul, + ol { + font-family: sans-serif; + font-size: 16px; + font-weight: normal; + margin: 0; + margin-bottom: 15px; + } + + p li, + ul li, + ol li { + list-style-position: inside; + margin-left: 5px; + } + + a { + color: #3498db; + text-decoration: underline; + } + + /* ------------------------------------- + BUTTONS + ------------------------------------- */ + .btn { + box-sizing: border-box; + width: 100%; + } + + .btn>tbody>tr>td { + padding-bottom: 15px; + } + + .btn table { + width: auto; + } + + .btn table td { + background-color: #ffffff; + border-radius: 5px; + text-align: center; + } + + .btn a { + display: block; + font-size: 16px !important; + width: 100%; + text-align: center; + color: #ffffff; + background-color: #5942c1; + border: 0; + border-radius: 50rem; + box-sizing: border-box; + cursor: pointer; + text-decoration: none; + font-size: 12px; + margin: 0; + padding: 12px 25px; + } + + .btn-primary table td { + background-color: #3498db; + } + + .btn-primary a { + background-color: #3498db; + border-color: #3498db; + color: #ffffff; + } + + /* ------------------------------------- + OTHER STYLES THAT MIGHT BE USEFUL + ------------------------------------- */ + .last { + margin-bottom: 0; + } + + .first { + margin-top: 0; + } + + .align-center { + text-align: center; + } + + .align-right { + text-align: right; + } + + .align-left { + text-align: left; + } + + .clear { + clear: both; + } + + .mt0 { + margin-top: 0; + } + + .mb0 { + margin-bottom: 0; + } + + .preheader { + color: transparent; + display: none; + height: 0; + max-height: 0; + max-width: 0; + opacity: 0; + overflow: hidden; + mso-hide: all; + visibility: hidden; + width: 0; + } + + .powered-by a { + text-decoration: none; + } + + hr { + border: 0; + border-bottom: 1px solid #f6f6f6; + margin: 20px 0; + } + + /* ------------------------------------- + RESPONSIVE AND MOBILE FRIENDLY STYLES + ------------------------------------- */ + @media only screen and (max-width: 620px) { + table.body h1 { + font-size: 28px !important; + margin-bottom: 10px !important; + } + + table.body p, + table.body ul, + table.body ol, + table.body td, + table.body span, + table.body a { + font-size: 16px !important; + } + + table.body .wrapper, + table.body .article { + padding: 10px !important; + } + + table.body .content { + padding: 0 !important; + } + + table.body .container { + padding: 0 !important; + width: 100% !important; + } + + table.body .main { + border-left-width: 0 !important; + border-radius: 0 !important; + border-right-width: 0 !important; + } + + table.body .btn table { + width: 100% !important; + } + + table.body .btn a { + width: 100% !important; + } + + table.body .img-responsive { + height: auto !important; + max-width: 100% !important; + width: auto !important; + } + } + + /* ------------------------------------- + PRESERVE THESE STYLES IN THE HEAD + ------------------------------------- */ + @media all { + .ExternalClass { + width: 100%; + } + + .ExternalClass, + .ExternalClass p, + .ExternalClass span, + .ExternalClass font, + .ExternalClass td, + .ExternalClass div { + line-height: 100%; + } + + .apple-link a { + color: inherit !important; + font-family: inherit !important; + font-size: inherit !important; + font-weight: inherit !important; + line-height: inherit !important; + text-decoration: none !important; + } + + #MessageViewBody a { + color: inherit; + text-decoration: none; + font-size: inherit; + font-family: inherit; + font-weight: inherit; + line-height: inherit; + } + + .btn-primary table td:hover { + background-color: #34495e !important; + } + + .btn-primary a:hover { + background-color: #34495e !important; + border-color: #34495e !important; + } + } + </style> +</head> + +<body> + <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"> + <tr> + <td> </td> + <td class="container"> + <div class="content"> + <!-- START CENTERED WHITE CONTAINER --> + <table role="presentation" class="main"> + <!-- START MAIN CONTENT AREA --> + <tr> + <td class="wrapper"> + <table role="presentation" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td> + <p style="font-size: 18px;">Hi!</p> + <p style="font-size: 21px;">A sign in is requested for your account.</p> + <p> + <strong style="font-size: 21px;"> <%= otp %> </strong> + </p> + <p> + This one-time password will expire after 10 minutes. + </p> + </td> + </tr> + </table> + </td> + </tr> + + <!-- END MAIN CONTENT AREA --> + </table> + <!-- END CENTERED WHITE CONTAINER --> + + <!-- START FOOTER --> + <div class="footer"> + <table role="presentation" border="0" cellpadding="0" cellspacing="0"> + + <tr> + <td class="content-block powered-by"> + <img src="<%= baseUrl %>/img/logo.png" width="60" alt="THX Logo"> + </td> + </tr> + </table> + </div> + <!-- END FOOTER --> + </div> + </td> + <td> </td> + </tr> + </table> +</body> + +</html> \ No newline at end of file diff --git a/apps/auth/src/assets/views/mail/email-verify.ejs b/apps/auth/src/assets/views/mail/email-verify.ejs new file mode 100644 index 000000000..f6ce55b8e --- /dev/null +++ b/apps/auth/src/assets/views/mail/email-verify.ejs @@ -0,0 +1,418 @@ +<!DOCTYPE html> +<html> + +<head> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title>Confirm your E-mail Address</title> + <style> + /* ------------------------------------- + GLOBAL RESETS + ------------------------------------- */ + + img { + border: none; + -ms-interpolation-mode: bicubic; + max-width: 100%; + } + + body { + background-color: #f6f6f6; + font-family: sans-serif; + -webkit-font-smoothing: antialiased; + font-size: 14px; + line-height: 1.4; + margin: 0; + padding: 0; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + } + + table { + border-collapse: separate; + mso-table-lspace: 0pt; + mso-table-rspace: 0pt; + width: 100%; + } + + table td { + font-family: sans-serif; + font-size: 14px; + vertical-align: top; + } + + /* ------------------------------------- + BODY & CONTAINER + ------------------------------------- */ + + .body { + background-color: #f6f6f6; + width: 100%; + } + + /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ + .container { + display: block; + margin: 0 auto !important; + /* makes it centered */ + max-width: 580px; + padding: 10px; + width: 580px; + } + + /* This should also be a block element, so that it will fill 100% of the .container */ + .content { + box-sizing: border-box; + display: block; + margin: 0 auto; + max-width: 580px; + padding: 10px; + } + + /* ------------------------------------- + HEADER, FOOTER, MAIN + ------------------------------------- */ + .main { + background: #ffffff; + border-radius: 3px; + width: 100%; + } + + .wrapper { + box-sizing: border-box; + padding: 20px; + } + + .content-block { + padding-bottom: 10px; + padding-top: 10px; + } + + .footer { + clear: both; + margin-top: 10px; + text-align: center; + width: 100%; + } + + .footer td, + .footer p, + .footer span, + .footer a { + color: #999999; + font-size: 12px; + text-align: center; + } + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1, + h2, + h3, + h4 { + color: #000000; + font-family: sans-serif; + font-weight: 400; + line-height: 1.4; + margin: 0; + margin-bottom: 30px; + } + + h1 { + font-size: 35px; + font-weight: 300; + text-align: center; + text-transform: capitalize; + } + + p, + ul, + ol { + font-family: sans-serif; + font-size: 16px; + font-weight: normal; + margin: 0; + margin-bottom: 15px; + } + + p li, + ul li, + ol li { + list-style-position: inside; + margin-left: 5px; + } + + a { + color: #3498db; + text-decoration: underline; + } + + /* ------------------------------------- + BUTTONS + ------------------------------------- */ + .btn { + box-sizing: border-box; + width: 100%; + } + + .btn>tbody>tr>td { + padding-bottom: 15px; + } + + .btn table { + width: auto; + } + + .btn table td { + background-color: #ffffff; + border-radius: 5px; + text-align: center; + } + + .btn a { + display: block; + font-size: 16px !important; + width: 100%; + text-align: center; + color: #ffffff; + background-color: #5942c1; + border: 0; + border-radius: 50rem; + box-sizing: border-box; + cursor: pointer; + text-decoration: none; + font-size: 12px; + margin: 0; + padding: 12px 25px; + } + + .btn-primary table td { + background-color: #3498db; + } + + .btn-primary a { + background-color: #3498db; + border-color: #3498db; + color: #ffffff; + } + + /* ------------------------------------- + OTHER STYLES THAT MIGHT BE USEFUL + ------------------------------------- */ + .last { + margin-bottom: 0; + } + + .first { + margin-top: 0; + } + + .align-center { + text-align: center; + } + + .align-right { + text-align: right; + } + + .align-left { + text-align: left; + } + + .clear { + clear: both; + } + + .mt0 { + margin-top: 0; + } + + .mb0 { + margin-bottom: 0; + } + + .preheader { + color: transparent; + display: none; + height: 0; + max-height: 0; + max-width: 0; + opacity: 0; + overflow: hidden; + mso-hide: all; + visibility: hidden; + width: 0; + } + + .powered-by a { + text-decoration: none; + } + + hr { + border: 0; + border-bottom: 1px solid #f6f6f6; + margin: 20px 0; + } + + /* ------------------------------------- + RESPONSIVE AND MOBILE FRIENDLY STYLES + ------------------------------------- */ + @media only screen and (max-width: 620px) { + table.body h1 { + font-size: 28px !important; + margin-bottom: 10px !important; + } + + table.body p, + table.body ul, + table.body ol, + table.body td, + table.body span, + table.body a { + font-size: 16px !important; + } + + table.body .wrapper, + table.body .article { + padding: 10px !important; + } + + table.body .content { + padding: 0 !important; + } + + table.body .container { + padding: 0 !important; + width: 100% !important; + } + + table.body .main { + border-left-width: 0 !important; + border-radius: 0 !important; + border-right-width: 0 !important; + } + + table.body .btn table { + width: 100% !important; + } + + table.body .btn a { + width: 100% !important; + } + + table.body .img-responsive { + height: auto !important; + max-width: 100% !important; + width: auto !important; + } + } + + /* ------------------------------------- + PRESERVE THESE STYLES IN THE HEAD + ------------------------------------- */ + @media all { + .ExternalClass { + width: 100%; + } + + .ExternalClass, + .ExternalClass p, + .ExternalClass span, + .ExternalClass font, + .ExternalClass td, + .ExternalClass div { + line-height: 100%; + } + + .apple-link a { + color: inherit !important; + font-family: inherit !important; + font-size: inherit !important; + font-weight: inherit !important; + line-height: inherit !important; + text-decoration: none !important; + } + + #MessageViewBody a { + color: inherit; + text-decoration: none; + font-size: inherit; + font-family: inherit; + font-weight: inherit; + line-height: inherit; + } + + .btn-primary table td:hover { + background-color: #34495e !important; + } + + .btn-primary a:hover { + background-color: #34495e !important; + border-color: #34495e !important; + } + } + </style> +</head> + +<body> + <span class="preheader">This is preheader text. Some clients will show this text as a preview.</span> + <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"> + <tr> + <td> </td> + <td class="container"> + <div class="content"> + <!-- START CENTERED WHITE CONTAINER --> + <table role="presentation" class="main"> + <!-- START MAIN CONTENT AREA --> + <tr> + <td class="wrapper"> + <table role="presentation" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td> + <p style="font-size: 18px;">Hi!</p> + <p style="font-size: 21px;">Please confirm your e-mail address.</p> + <p> + Complete the verification by clicking on the button below. If you need + any help please do not hesitate to contact and hop into our Discord or + Slack channels. + </p> + + <p> + <div class="btn"> + <a href="<%= verifyURL %>" target="_blank"> + Confirm + <img width="12" height="auto" src="<%= baseUrl %>/img/mail/icons/chevron-right-solid.png" /> + </a> + </div> + </p> + </td> + </tr> + </table> + </td> + </tr> + + <!-- END MAIN CONTENT AREA --> + </table> + <!-- END CENTERED WHITE CONTAINER --> + + <!-- START FOOTER --> + <div class="footer"> + <table role="presentation" border="0" cellpadding="0" cellspacing="0"> + + <tr> + <td class="content-block powered-by"> + <img src="<%= baseUrl %>/img/logo.png" width="60" alt="THX Logo"> + </td> + </tr> + </table> + </div> + <!-- END FOOTER --> + </div> + </td> + <td> </td> + </tr> + </table> +</body> + +</html> \ No newline at end of file diff --git a/apps/auth/src/assets/views/otp.ejs b/apps/auth/src/assets/views/otp.ejs new file mode 100644 index 000000000..17e92f940 --- /dev/null +++ b/apps/auth/src/assets/views/otp.ejs @@ -0,0 +1,90 @@ +<script type="module" src="/js/otp.js?v=<%= locals.deployedAt %>"></script> +<% if (params.backgroundImgUrl) { %> +<style type="text/css"> + body { + background-image: url("<%- params.backgroundImgUrl %>") !important; + background-repeat: no-repeat; + background-size: cover; + } +</style> +<% } %> +<style type="text/css"> + @keyframes shine { + to { + background-position-x: -200%; + } + } + + .card { + border: 0; + } + + .card .card-header { + padding: 0; + height: 5px; + } + + .card.is-loading .card-header { + background: linear-gradient(90deg, rgba(0, 0, 0, 0.03) 10%, #5942c1 10%, #5942c1 90%, rgba(0, 0, 0, 0.03) 90%); + background-size: 200% 100%; + animation: 1.5s shine linear infinite; + } + + .form-control-otp { + text-align: center; + font-weight: bold; + color: #5942c1; + } +</style> +<div class="col-md-6 offset-md-3 col-xl-4 offset-xl-4 order-0 order-sm-1"> + <div class="text-center pb-4 pt-3"> + <% if (params.logoImgUrl) { %> + <img src="<%- params.logoImgUrl %>" width="60" alt="THX Logo" /> + <% } else { %> + <img src="/img/logo.png" width="60" alt="THX Logo" /> + <% } %> + </div> + <div class="card shadow-sm" :class="{ 'is-loading': isLoading }"> + <div class="card-header"></div> + <div class="card-body p-sm-4 pb-sm-0"> + + <% if (alert && alert.message) { %> + <div class="alert alert-<%= alert.variant %>"> + <i class="fas fa-<%= alert.icon %> mr-2"></i> + <%- alert.message %> + </div> + <% } %> + + <form id="form-otp" action="/oidc/<%= uid %>/signin/otp" method="post" class="mt-3"> + <div class="form-group"> + <p class="mt-2 d-flex align-items-center justify-content-between"> + <span class=" text-muted"> + One-time password + </span> + <button form="otp-resend" type="submit" class="btn btn-sm btn-link p-0"> + Send e-mail again + </button> + </p> + <div class="d-flex justify-content-between"> + <input v-model="otp" @input="onInput" style="letter-spacing: 1rem;" + class="form-control form-control-otp" placeholder="*****" :disabled="isLoading" + type="text" name="otp" inputmode="numeric" required> + </div> + </div> + <input type="hidden" name="otp" :value="otp" /> + <input type="hidden" name="returnUrl" value="<%= params.return_url %>" /> + <button ref="submit" form="form-otp" type="submit" class="d-none"> Sign in </button> + </form> + <form id="otp-resend" action="/oidc/<%= uid %>/signin/resend-otp" method="post"></form> + </div> + <div class="card-footer justify-content-end d-flex align-items-center"> + <a class="text-muted mr-auto" href="/oidc/<%= uid %>/signin">Return to app</a> + <div> + <a href="https://discord.com/invite/TzbbSmkE7Y" class="text-muted">Help</a> + <a href="https://thx.network/privacy.pdf" target="_blank" class="text-muted ml-3">Privacy</a> + <a href="https://thx.network/general-terms-and-conditions.pdf" target="_blank" + class="text-muted ml-3">Terms</a> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/apps/auth/src/assets/views/signin.ejs b/apps/auth/src/assets/views/signin.ejs new file mode 100644 index 000000000..93e67391c --- /dev/null +++ b/apps/auth/src/assets/views/signin.ejs @@ -0,0 +1,369 @@ +<script async type="module" src="/js/signin.js?v=<%= locals.deployedAt %>"></script> +<% if (params.backgroundImgUrl) { %> +<style type="text/css"> + body { + background-image: url("<%- params.backgroundImgUrl %>") !important; + background-repeat: no-repeat; + background-size: cover; + } +</style> +<% } %> +<style type="text/css"> + @keyframes shine { + to { + background-position-x: -200%; + } + } + + hr { + margin: 2rem 0; + position: relative; + } + + hr.or-separator:after { + content: 'OR'; + background: white; + color: gray; + width: 40px; + font-size: .8rem; + margin-top: -.7rem; + margin-left: -20px; + left: 50%; + text-align: center; + position: absolute; + } + + .btn-discord { + background-color: #5865F2; + color: white; + border-radius: 25px; + height: 50px; + justify-content: center; + } + + .btn-discord:hover { + background-color: #3f4abe; + color: white; + } + + .card { + border: 0; + } + + .fade-in { + opacity: 0; + transition: opacity ease .5s; + } + + .card .card-header { + padding: 0; + height: 5px; + } + + .fade-in.is-mounted { + opacity: 1; + } + + .card.is-loading .card-header { + background: linear-gradient(90deg, rgba(0, 0, 0, 0.03) 10%, #5942c1 10%, #5942c1 90%, rgba(0, 0, 0, 0.03) 90%); + background-size: 200% 100%; + animation: 1.5s shine linear infinite; + } + + .premium-link:hover { + color: white; + text-decoration: none; + } +</style> +<div class="<%- (params.isSignup) ? 'col-md-10 col-xl-10 offset-md-1 offset-xl-2' : 'col-md-8 offset-md-2 col-xl-4 offset-xl-4' %> fade-in" + @vue:mounted="onMounted" :class="{ 'is-mounted': isMounted }"> + + <div class="text-center pb-4 pt-3"> + <% if (params.logoImgUrl) { %> + <img src="<%- params.logoImgUrl %>" width="60" alt="THX Logo" /> + <% } else { %> + <img src="/img/logo.png" width="60" alt="THX Logo" /> + <% } %> + </div> + + <div class="card shadow-sm" :class="{ 'is-loading': isLoading, 'is-mounted': isMounted }"> + <div class="card-header"></div> + + <% if (params.logoImgUrl) { %> + <div class="bg-light py-2 px-4 d-flex align-items-center"> + <img src="/img/logo.png" width="18" alt="THX Logo" class="mr-2" /> + <small class="text-muted">Sign in with THX Network</small> + </div> + <% } %> + + <div class="row"> + <% if(params.isSignup) { %> + <div class="col-md-6 order-md-1"> + <div class="bg-dark p-sm-4 h-100"> + + <% + var premium = []; + + if (params.signup_plan == 1) { + premium.push({ + class: 'show', + iconClass: 'fa-check-circle text-success', + title: '€ 89 / month (14 day free trial)', + description: + 'We invite you to try out all of the above for a month, with no obligation to pay unless you\'re satisfied!', + }); + }; + + if (params.signup_plan == 2) { + premium.push({ + class: 'show', + iconClass: 'fa-check-circle text-success', + title: '€ 449 / month (14 day free trial)', + description: 'We invite you to try out all of the above for a month, with no obligation to pay unless you\'re satisfied!', + }); + }; + + + premium.push({ + class: '', + iconClass: 'fa-check-circle text-success', + title: '2,5% fee on rewards', + description: 'We charge a small % transactional fee on coin rewards (ERC-20). Your end users pay no fees.', + }); + + premium.push({ + class: '', + iconClass: 'fa-times-circle text-danger', + title: 'Transaction costs (gas fees)', + description: 'We cover all user transaction costs (gas fees) up to the plans reward limit.', + }); + + if (params.signup_plan == 1) { + premium.push({ + class: '', + iconClass: 'fa-times-circle text-danger', + title: 'Setup fee', + description: 'We require no setup fee. Sign-up and you\'re ready to go!', + }); + + premium.push({ + class: '', + iconClass: 'fa-check-circle text-success', + title: 'Discord Bot', + description: 'Reach your community in channels that matter most. Post reward overviews and announce new rewards, all automated.', + }); + + premium.push({ + class: '', + iconClass: 'fa-check-circle text-success', + title: 'Twitter Automation', + description: 'You are already very busy managing campaigns... We\'ll automate your Twitter campaigns, so you can focus on growth!', + }); + }; + + if (params.signup_plan == 2) { + premium.push({ + class: '', + iconClass: 'fa-times-circle text-danger', + title: 'Setup fee', + description: 'We require no setup fee. Sign-up and you\'re ready to go!', + }); + + premium.push({ + class: '', + iconClass: 'fa-check-circle text-success', + title: 'Dedicated Campaign Manager', + description: + 'We understand you are a busy person! That\'s why we offer you a dedicated campaign manager to during your free first month.', + }); + + premium.push({ + class: '', + iconClass: 'fa-check-circle text-success', + title: 'Unlimited Technical Support', + description: + 'Your devs are probably working on other important tasks. Let our devs talk to your devs to make engine integration a piece of cake.', + }); + }; + + premium.push({ + class: '', + iconClass: 'fa-check-circle text-success', + title: 'Weekly Performance Reports', + description: + 'We know budgets and results are important to you. We send you weekly reports by mail and are happy to jump on a call to discuss results and potential improvements.', + }); + + %> + + <ul class="text-muted list-unstyled"> + <% for(var i=0; i < premium.length; i++) { %> + <li> + <a class="text-white d-flex premium-link py-1" data-toggle="collapse" href="#collapseExample<%= i %>" + role="button" aria-expanded="false" aria-controls="collapseExample<%= i %>"> + <div> + <i class="fas mr-1 <%- premium[i].iconClass %>"></i> + <span><%- premium[i].title %></span> + </div> + <i class=" fas fa-caret-down text-muted ml-auto"></i> + </a> + <div class="collapse <%- premium[i].class %>" id="collapseExample<%= i %>"> + <div class="card card-body bg-darker my-2"> + <%- premium[i].description %> + </div> + </div> + </li> + <% } %> + </ul> + + <hr class="bg-gray" /> + + <p class="text-muted small"> + By continuing you accept THX Network's <a class="text-white" + href="https://thx.network/general-terms-and-conditions.pdf" target="_blank">Terms & Conditions</a> + and <a class="text-white" href="https://thx.network/privacy-policy.pdf" target="_blank">Privacy + Policy</a>. + </p> + + </div> + </div> + <% } %> + + <div class="<%- (params.isSignup) ? 'col-md-6 order-md-0' : 'col-md-12' %>"> + <div class="card-body p-sm-4 pb-sm-0"> + + <% if (params.signup_offer == 'true' && params.signup_plan == '2') { %> + <div class="alert alert-info"> + <i class="fas fa-info-circle mr-1"></i> + You have received a + <strong><%- (params.signup_plan == '1') ? 'Basic' : (params.signup_plan == '2') ? 'Premium' : 'Free' %> + Offer 🎁 </strong> + </div> + <% } %> + + <% if (params.authRequestMessage) { %> + <div v-if="alert && alert.message" class="alert" :class="`alert-${alert.variant}`"> + <i class="fas fa-exclamation-circle mr-2"></i> + {{alert.message}} + </div> + <% } %> + + <% if(params.referral_code) { %> + <div class="alert alert-info"> + <i class="fas fa-comments mr-2"></i> We have detected a referral code! + </div> + <% } %> + + <% if (locals.alert && alert.message) { %> + <div class="alert alert-<%= alert.variant %>"> + <%- alert.message %> + </div> + <% } %> + + <% if (params.isSignup) { %> + <h2 class="h3 mb-3 font-weight-normal text-dark">Start your <strong>free trial</strong>!</h2> + <% } %> + + <% if (params.discordLoginUrl && params.isSignup) { %> + <p class="text-muted">Continue with Discord to get instant access to the <a + href="https://discord.com/invite/TzbbSmkE7Y">#support</a> channel 🙋.</p> + <a class="mb-2 d-flex align-items-center btn btn-discord" href="<%= params.discordLoginUrl %>"> + <img width="20" class="mr-2" src="/img/discord-logo.png" alt="Discord logo" /> + <span>Continue with Discord</span> + <i class="fas fa-chevron-right" aria-hidden="true"></i> + </a> + <hr class="or-separator" /> + <% } %> + + <form id="form-signin" action="/oidc/<%= uid %>/signin" method="post"> + <% if(params.emailPasswordEnabled) { %> + <p class="text-muted"> + <%- (params.isSignup) ? 'Use your work e-mail' : 'Use your e-mail' %> + </p> + <div class="form-group"> + <input placeholder="<%- (params.isSignup) ? 'yourname@work-email.com' : 'yourname@example.com' %>" + class="form-control" class="form-control" required type="email" name="email" v-model="email" + :class="{ + 'is-valid': email && !isDisabled, 'is-invalid': email && isDisabled + }"> + </div> + <% } %> + + <% if (params.mfaEnabled) { %> + <label>MFA code:</label> + <input class="form-control mb-4" required name="code"> + <% } %> + + <input type="hidden" name="signupEmail" value="<%= params.signup_email %>" /> + <input type="hidden" name="returnUrl" value="<%= params.return_url %>" /> + <input type="hidden" name="isWidget" value="<%= params.isWidget %>" /> + + <% if(params.emailPasswordEnabled) { %> + <button type="submit" class="btn rounded-pill btn-dark btn- btn-block mb-4" + :class="{'disabled': isDisabled || isLoading}" @click="onClickSubmit"> + <%- (params.isSignup) ? 'Continue with e-mail' : 'Send one-time password'%> + <i class="fas fa-chevron-right" aria-hidden="true"></i><br /> + </button> + <% } %> + + </form> + + <% if (params.trustedProviderAvailable) { %> + <hr class="or-separator" /> + <% } %> + + <% if (params.trustedProviderAvailable) { %> + <p class="text-muted">Use a trusted provider</p> + <% } %> + + <div class="d-flex align-items-center justify-content-start flex-wrap"> + <% if (params.googleLoginUrl) { %> + <a style="width: 40px; height: 40px;" class="mr-2 mb-2 d-block p-1 btn btn-light" + href="<%= params.googleLoginUrl %>" data-toggle="tooltip" title="Sign in with Google"> + <img width="20" src="/img/g-logo.png" alt="Google logo" /> + </a> + <% } %> + + <% if (params.twitterLoginUrl) { %> + <a style="width: 40px; height: 40px;" class="mr-2 mb-2 d-block p-1 btn btn-light" + href="<%= params.twitterLoginUrl %>" data-toggle="tooltip" title="Sign in with Twitter"> + <img width="20" src="/img/t-logo.png" alt="Twitter logo" /> + </a> + <% } %> + + <% if (params.discordLoginUrl && !params.isSignup) { %> + <a style="width: 40px; height: 40px;" class="mr-2 mb-2 d-block p-1 btn btn-light" + href="<%= params.discordLoginUrl %>" data-toggle="tooltip" title="Sign in with Discord"> + <img width="20" src="/img/discord-logo.png" alt="Discord logo" /> + </a> + <% } %> + + <% if (params.githubLoginUrl) { %> + <a style="width: 40px; height: 40px;" class="mr-2 mb-2 d-block p-1 btn btn-light" + href="<%= params.githubLoginUrl %>" data-toggle="tooltip" title="Sign in with Github"> + <img width="20" src="/img/github-logo.png" alt="github logo" /> + </a> + <% } %> + + <% if (params.twitchLoginUrl) { %> + <a style="width: 40px; height: 40px;" class="mr-2 mb-2 d-block p-1 btn btn-light" + href="<%= params.twitchLoginUrl %>" data-toggle="tooltip" title="Sign in with Twitch"> + <img width="20" src="/img/twitch-logo.png" alt="twitch logo" /> + </a> + <% } %> + </div> + + </div> + </div> + </div> + + + <div class="card-footer d-flex align-items-center justify-content-end"> + <a @click="onClickReturn" href="#" class="text-muted mr-auto cursor-pointer">Return to app</a> + <a href="https://discord.com/invite/TzbbSmkE7Y" class="text-muted">Help</a> + <a href="https://thx.network/privacy-policy.pdf" target="_blank" class="text-muted ml-3">Privacy</a> + <a href="https://thx.network/general-terms-and-conditions.pdf" target="_blank" class="text-muted ml-3">Terms</a> + </div> + + </div> +</div> \ No newline at end of file diff --git a/apps/auth/src/assets/views/totp.ejs b/apps/auth/src/assets/views/totp.ejs new file mode 100644 index 000000000..edef4b75e --- /dev/null +++ b/apps/auth/src/assets/views/totp.ejs @@ -0,0 +1,36 @@ +<div class="col-md-6 offset-md-3 order-0 order-sm-1"> + <div class="text-center pb-4 pt-4"> + <img src="/img/logo.png" width="60" alt="THX Logo" /> + </div> + <div class="card shadow-sm mb-5 w-100"> + <div class="card-body p-sm-5"> + <strong class="h3 d-block text-dark text-center mb-5"> + MFA Configuration + </strong> + <p>Scan this QR code with your authenticator app. E.g. <a + href="https://apps.apple.com/us/app/google-authenticator/id388497605" target="_blank">Google + Authenticator</a> or <a href="https://www.authy.com" target="_blank">Authy</a>.</p> + <hr> + <div class="img-responsive text-center bg-light p-3"> + <img width="200" src="<%= params.qrCode %>" /> + </div> + <hr> + <form autocomplete="off" action="/oidc/<%= uid %>/account/totp" method="post" class="mb-3"> + <div class="p-2"> + <p>Enter the code shown in your authenticator app here:</p> + <input placeholder="123123" class="form-control" autocomplete="off" required name="code" /> + <input type="hidden" name="otpSecret" value="<%= params.otpSecret %>" /> + </div> + <hr> + <button type="submit" class="btn rounded-pill btn-primary btn-block"> + Submit code + <i class="fas fa-chevron-right" aria-hidden="true"></i> + </button> + </form> + + <a class="btn btn-link btn-block rounded-pill" href="/oidc/<%= uid %>/account"> + Return to Account + </a> + </div> + </div> +</div> \ No newline at end of file diff --git a/apps/auth/src/environments/environment.prod.ts b/apps/auth/src/environments/environment.prod.ts new file mode 100644 index 000000000..c9669790b --- /dev/null +++ b/apps/auth/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/auth/src/environments/environment.ts b/apps/auth/src/environments/environment.ts new file mode 100644 index 000000000..a20cfe557 --- /dev/null +++ b/apps/auth/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +}; diff --git a/apps/auth/src/main.ts b/apps/auth/src/main.ts new file mode 100644 index 000000000..896a6d371 --- /dev/null +++ b/apps/auth/src/main.ts @@ -0,0 +1,53 @@ +import 'newrelic'; +import http from 'http'; +import https from 'https'; +import app from './app/app'; +import db from './app/util/database'; +import { createTerminus } from '@godaddy/terminus'; +import { healthCheck } from './app/util/healthcheck'; +import { logger } from './app/util/logger'; +import fs from 'fs'; +import path from 'path'; +import { LOCAL_CERT, LOCAL_CERT_KEY } from './app/config/secrets'; + +let server; +if (LOCAL_CERT && LOCAL_CERT_KEY) { + server = https.createServer( + { + key: fs.readFileSync(path.resolve(path.dirname(__dirname), LOCAL_CERT_KEY)), + cert: fs.readFileSync(path.resolve(path.dirname(__dirname), LOCAL_CERT)), + }, + app, + ); +} else { + server = http.createServer(app); +} + +const options = { + healthChecks: { + '/healthcheck': healthCheck, + 'verbatim': true, + }, + onSignal: () => { + logger.info('Server shutting down'); + return Promise.all([db.disconnect()]); + }, + logger: logger.error, +}; + +createTerminus(server, options); + +process.on('uncaughtException', function (err: Error) { + if (err) { + logger.error({ + message: 'Uncaught Exception was thrown, shutting down', + errorName: err.name, + errorMessage: err.message, + stack: err.stack, + }); + process.exit(1); + } +}); + +logger.info(`Server is starting on port: ${app.get('port')}, env: ${app.get('env')}`); +server.listen(app.get('port')); diff --git a/apps/auth/tsconfig.app.json b/apps/auth/tsconfig.app.json new file mode 100644 index 000000000..0e2bb5eac --- /dev/null +++ b/apps/auth/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.test.ts"], + "include": ["package.json", "src/**/*.ts", "../../libs/common/src/**/*"] +} diff --git a/apps/auth/tsconfig.json b/apps/auth/tsconfig.json new file mode 100644 index 000000000..d2b6e8e03 --- /dev/null +++ b/apps/auth/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + // Custom + "noImplicitAny": false, + "strict": false + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/auth/tsconfig.spec.json b/apps/auth/tsconfig.spec.json new file mode 100644 index 000000000..5692087fb --- /dev/null +++ b/apps/auth/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + // Custom + "allowJs": true + }, + "exclude": ["../../libs/common/src/lib/scss/**/*"], + "include": [ + "jest.config.ts", + "**/*.test.ts", + "**/*.d.ts", + "src/**/*.ts", + "../../libs/common/src/**/*", + "../../libs/sdk/src/**/*", + "src/app/hardhat/export/*.json" + ] +} diff --git a/apps/auth/webpack.config.js b/apps/auth/webpack.config.js new file mode 100644 index 000000000..29f653d71 --- /dev/null +++ b/apps/auth/webpack.config.js @@ -0,0 +1,13 @@ +const { composePlugins, withNx } = require('@nx/webpack'); + +// Nx plugins for webpack. +module.exports = composePlugins( + withNx({ + target: 'node', + }), + (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + return config; + }, +); diff --git a/apps/campaign/src/components/card/BaseCardMembership.vue b/apps/campaign/src/components/card/BaseCardMembership.vue deleted file mode 100644 index 16dc22142..000000000 --- a/apps/campaign/src/components/card/BaseCardMembership.vue +++ /dev/null @@ -1,48 +0,0 @@ -<template> - <div class="text-center"> - <div class="position-relative rounded-circle m-auto gradient-border-xl" style="width: 75px; height: 75px"> - <div - class="position-relative bg-dark rounded-circle d-flex align-items-center justify-content-center" - style="z-index: 1; width: 65px; height: 65px" - > - <i class="fas fa-id-card" style="font-size: 1.5rem" /> - </div> - </div> - <b-badge class="mt-2 p-2" variant="primary"> - <i class="fas fa-trophy me-1 text-opaque" /> - {{ membership }} - </b-badge> - </div> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; -import { mapStores } from 'pinia'; -import { useLiquidityStore } from '../../stores/Liquidity'; -import { useVeStore } from '../../stores/VE'; -import { fromWei } from 'web3-utils'; - -export default defineComponent({ - name: 'BaseCardMembership', - data() { - return {}; - }, - computed: { - ...mapStores(useVeStore, useLiquidityStore), - membership() { - if (!this.veStore.lock) return; - const price = this.liquidityStore.pricing['20USDC-80THX']; - const amount = Number(fromWei(String(this.veStore.lock.amount))); - const amountInUSD = amount * price; - - if (amountInUSD < 5) return 'No Rank'; - if (amountInUSD > 5 && amountInUSD < 50) return 'Rookie'; - if (amountInUSD > 50 && amountInUSD < 500) return 'Pro'; - if (amountInUSD > 500 && amountInUSD < 5000) return 'Elite'; - if (amountInUSD > 5000 && amountInUSD < 50000) return 'Master'; - if (amountInUSD > 50000 && amountInUSD < 500000) return 'Legend'; - }, - }, - methods: {}, -}); -</script> diff --git a/docker-compose.api.yml b/docker-compose.api.yml new file mode 100644 index 000000000..fd2a38713 --- /dev/null +++ b/docker-compose.api.yml @@ -0,0 +1,32 @@ +version: '3.8' + +# This compose file only works when used in conjunction with the default docker-compose file. +# docker compose -f docker-compose.yml -f docker-compose.api.yml + +services: + api: + build: + context: . + dockerfile: apps/api/Dockerfile + target: develop + volumes: + - ./coverage/apps/api:/usr/src/app/coverage/apps/api + extra_hosts: + - "host.docker.internal:host-gateway" + env_file: + - ./apps/api/.env.example + environment: + MONGODB_URI: "mongodb://root:root@mongo:27017/api?authSource=admin&ssl=false" + MONGODB_URI_TEST_OVERRIDE: "mongodb://root:root@mongo:27017/api_test?authSource=admin&ssl=false" + AWS_S3_PUBLIC_BUCKET_NAME: "test-thx-storage-bucket" + AWS_S3_PRIVATE_BUCKET_NAME: "test-thx-private-storage-bucket" + HARDHAT_RPC: "http://host.docker.internal:8545" + HARDHAT_RPC_TEST_OVERRIDE: "http://host.docker.internal:8545" + SAFE_TXS_SERVICE: "http://host.docker.internal:8000/txs" + LOCAL_CERT: "" + LOCAL_CERT_KEY: "" + CWD: "/usr/src/app/apps/api/src/" + ports: + - 3001:3000 + depends_on: + - mongo \ No newline at end of file diff --git a/docker-compose.auth.yml b/docker-compose.auth.yml new file mode 100644 index 000000000..d3b618a5b --- /dev/null +++ b/docker-compose.auth.yml @@ -0,0 +1,21 @@ +version: '3.8' + +# This compose file only works when used in conjunction with the default docker-compose file. +# docker compose -f docker-compose.yml -r docker-compose.auth.yml + +services: + auth: + container_name: thx_auth + build: + context: . + dockerfile: apps/auth/Dockerfile + target: develop + volumes: + - ./coverage/apps/auth:/usr/src/app/coverage/apps/auth + env_file: + - apps/auth/.env.example + - apps/auth/.env.ci + ports: + - 3031:3030 + depends_on: + - mongo \ No newline at end of file diff --git a/docker-compose.safe.yml b/docker-compose.safe.yml new file mode 100644 index 000000000..e82ed7acf --- /dev/null +++ b/docker-compose.safe.yml @@ -0,0 +1,115 @@ +version: '3.8' + +volumes: + nginx-shared-txs: + nginx-shared-cfg: + +services: + nginx: + image: nginx:alpine + ports: + - "${REVERSE_PROXY_PORT}:8000" + volumes: + - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - nginx-shared-txs:/nginx-txs + - nginx-shared-cfg:/nginx-cfg + depends_on: + - txs-web + - cfg-web + - cgw-web + + txs-db: + image: postgres:14-alpine + environment: + POSTGRES_PASSWORD: postgres + volumes: + - ./docker/data/txs-db:/var/lib/postgresql/data + + cfg-db: + image: postgres:14-alpine + environment: + POSTGRES_PASSWORD: postgres + volumes: + - ./docker/data/cfg-db:/var/lib/postgresql/data + + + # Safe Transaction Service + txs-redis: + image: redis:alpine + + txs-rabbitmq: + image: rabbitmq:alpine + + txs-web: + image: safeglobal/safe-transaction-service:${TXS_VERSION} + env_file: + - ./docker/env/txs.env + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - ETHEREUM_NODE_URL=${RPC_NODE_URL} + depends_on: + - txs-db + - txs-redis + working_dir: /app + volumes: + - nginx-shared-txs:/nginx + - ./scripts/insert_safe_master_copy.py:/app/safe_transaction_service/history/management/commands/insert_safe_master_copy.py + command: docker/web/run_web.sh + + txs-worker-indexer: &txs-worker + image: safeglobal/safe-transaction-service:${TXS_VERSION} + env_file: + - ./docker/env/txs.env + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - ETHEREUM_NODE_URL=${RPC_NODE_URL} + - RUN_MIGRATIONS=1 + - WORKER_QUEUES=default,indexing + depends_on: + - txs-db + - txs-redis + command: docker/web/celery/worker/run.sh + + txs-worker-contracts-tokens: + <<: *txs-worker + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - WORKER_QUEUES=contracts,tokens + - ETHEREUM_NODE_URL=${RPC_NODE_URL} + + txs-worker-notifications-webhooks: + <<: *txs-worker + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - WORKER_QUEUES=notifications,webhooks + - ETHEREUM_NODE_URL=${RPC_NODE_URL} + + txs-scheduler: + <<: *txs-worker + command: docker/web/celery/scheduler/run.sh + + # Safe Config Service + cfg-web: + image: safeglobal/safe-config-service:${CFG_VERSION} + tty: true + volumes: + - nginx-shared-cfg:/nginx + env_file: + - ./docker/env/cfg.env + depends_on: + - cfg-db + + # Safe Client Gateway + cgw-redis: + image: redis:alpine + + cgw-web: + image: safeglobal/safe-client-gateway:${CGW_VERSION} + env_file: + - ./docker/env/cgw.env + depends_on: + - cgw-redis diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..9bf102b2f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + mongo: + image: mongo + restart: always + ports: + - 27017:27017 + env_file: .env.example + environment: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: root + MONGO_INITDB_DATABASE: admin + volumes: + - mongo-data:/data/db + - ./fixture/db:/docker-entrypoint-initdb.d/fixture + - ./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh + +volumes: + mongo-data: \ No newline at end of file diff --git a/docker/env/cfg.env b/docker/env/cfg.env new file mode 100644 index 000000000..152e15ab4 --- /dev/null +++ b/docker/env/cfg.env @@ -0,0 +1,42 @@ +PYTHONDONTWRITEBYTECODE=true + +SECRET_KEY=insecure_key_for_dev + +DEBUG=true +ROOT_LOG_LEVEL=DEBUG + +DJANGO_ALLOWED_HOSTS="*" + +GUNICORN_BIND_PORT=8001 + +DOCKER_NGINX_VOLUME_ROOT=/nginx + +GUNICORN_BIND_SOCKET=unix:${DOCKER_NGINX_VOLUME_ROOT}/gunicorn.socket + +# NGINX_HOST_PORT=8080 + +NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/ + +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_NAME=postgres +POSTGRES_HOST=cfg-db +POSTGRES_PORT=5432 + +DOCKER_WEB_VOLUME=.:/app + +GUNICORN_WEB_RELOAD=false + +DJANGO_SUPERUSER_PASSWORD=admin +DJANGO_SUPERUSER_USERNAME=root +DJANGO_SUPERUSER_EMAIL=test@example.com +DJANGO_OTP_ADMIN=false + +DEFAULT_FILE_STORAGE=django.core.files.storage.FileSystemStorage + +FORCE_SCRIPT_NAME=/cfg/ + +CGW_URL=http://nginx:8000/cgw +CGW_FLUSH_TOKEN=some_random_token + +CSRF_TRUSTED_ORIGINS="http://localhost:8000" \ No newline at end of file diff --git a/docker/env/cgw.env b/docker/env/cgw.env new file mode 100644 index 000000000..726714ff6 --- /dev/null +++ b/docker/env/cgw.env @@ -0,0 +1,25 @@ +# More detailed description of the available values can be found: +# https://github.com/gnosis/safe-client-gateway/blob/main/.env.sample +CONFIG_SERVICE_URI=http://nginx:8000/cfg + +FEATURE_FLAG_NESTED_DECODING=true + +SCHEME=http +ROCKET_SECRET_KEY=tVqiPxyM9RPkTbcrciV6ZcIzvssv+kF9YvfPy/CBjBM= +ROCKET_LOG_LEVEL=normal +ROCKET_PORT=3666 +ROCKET_ADDRESS=0.0.0.0 +WEBHOOK_TOKEN=some_random_token +RUST_LOG=debug +LOG_ALL_ERROR_RESPONSES=true + +INTERNAL_CLIENT_CONNECT_TIMEOUT=10000 +SAFE_APP_INFO_REQUEST_TIMEOUT=10000 +CHAIN_INFO_REQUEST_TIMEOUT=15000 + + +REDIS_URI=redis://cgw-redis +REDIS_URI_MAINNET=redis://cgw-redis + +EXCHANGE_API_BASE_URI=http://api.exchangeratesapi.io/latest +EXCHANGE_API_KEY=your_exchange_rate_api_token \ No newline at end of file diff --git a/docker/env/txs.env b/docker/env/txs.env new file mode 100644 index 000000000..bf85a0038 --- /dev/null +++ b/docker/env/txs.env @@ -0,0 +1,12 @@ +PYTHONPATH=/app/ +DJANGO_SETTINGS_MODULE=config.settings.production +DJANGO_SECRET_KEY='Very-secure-secret-string' +DEBUG=0 +DATABASE_URL=psql://postgres:postgres@txs-db:5432/postgres +ETH_L2_NETWORK=1 +REDIS_URL=redis://txs-redis:6379/0 +CELERY_BROKER_URL=amqp://guest:guest@txs-rabbitmq/ +DJANGO_ALLOWED_HOSTS="*" +FORCE_SCRIPT_NAME=/txs/ + +CSRF_TRUSTED_ORIGINS="http://localhost:8000" \ No newline at end of file diff --git a/docker/env/ui.env b/docker/env/ui.env new file mode 100644 index 000000000..5041d1899 --- /dev/null +++ b/docker/env/ui.env @@ -0,0 +1,31 @@ +### Required variables ### +NEXT_PUBLIC_INFURA_TOKEN= +NEXT_PUBLIC_GATEWAY_URL_PRODUCTION=https://localhost:8000/cgw + +# infura token used by Safe Apps +NEXT_PUBLIC_SAFE_APPS_INFURA_TOKEN= + +# Transaction simulation +NEXT_PUBLIC_TENDERLY_SIMULATE_ENDPOINT_URL= +NEXT_PUBLIC_TENDERLY_PROJECT_NAME= +NEXT_PUBLIC_TENDERLY_ORG_NAME= + +# Flag to switch to the production environment (redirect urls, gateway url, etc) +NEXT_PUBLIC_IS_PRODUCTION=true + +### Optional variables ### +# These variables are required only if you require a certain feature in the interface (e.g. Portis wallet) +# Or overwrite the fallback values (set a different WalletConnect bridge) + +# Latest supported safe version, used for upgrade prompts +NEXT_PUBLIC_SAFE_VERSION=1.3.0 + +# Access keys +NEXT_PUBLIC_SENTRY_DSN= +NEXT_PUBLIC_BEAMER_ID= + +# Wallet specific variables +NEXT_PUBLIC_WC_BRIDGE= +NEXT_PUBLIC_FORTMATIC_KEY= +NEXT_PUBLIC_PORTIS_KEY= +NEXT_PUBLIC_CYPRESS_MNEMONIC= \ No newline at end of file diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 000000000..af8d45799 --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,128 @@ +# https://github.com/KyleAMathews/docker-nginx/blob/master/nginx.conf +# https://linode.com/docs/web-servers/nginx/configure-nginx-for-optimized-performance/ +# https://docs.gunicorn.org/en/stable/deploy.html + +worker_processes 1; + +events { + worker_connections 2000; # increase if you have lots of clients + accept_mutex off; # set to 'on' if nginx worker_processes > 1 + use epoll; # Enable epoll for Linux 2.6+ + # 'use kqueue;' to enable for FreeBSD, OSX +} + +http { + include mime.types; + # fallback in case we can't determine a type + default_type application/octet-stream; + sendfile on; + + ## Transaction Service + upstream txs_app_server { + # ip_hash; # For load-balancing + # + # fail_timeout=0 means we always retry an upstream even if it failed + # to return a good HTTP response + server unix:/nginx-txs/gunicorn.socket fail_timeout=0; + + # for a TCP configuration + # server web:8000 fail_timeout=0; + keepalive 32; + } + + ## Config service + upstream cfg_app_server { + ip_hash; # For load-balancing + # server cfg-web:8001 fail_timeout=0; + server unix:/nginx-cfg/gunicorn.socket fail_timeout=0; + # + # fail_timeout=0 means we always retry an upstream even if it failed + # to return a good HTTP response + keepalive 32; + } + + ## Client gateway + upstream cgw_app_server { + ip_hash; # For load-balancing + server cgw-web:3666 fail_timeout=0; + # + # fail_timeout=0 means we always retry an upstream even if it failed + # to return a good HTTP response + keepalive 32; + } + + server { + access_log off; + listen 8000 deferred; + charset utf-8; + keepalive_timeout 75s; + + # https://thoughts.t37.net/nginx-optimization-understanding-sendfile-tcp-nodelay-and-tcp-nopush-c55cdd276765 + # tcp_nopush on; + # tcp_nodelay on; + + gzip on; + gzip_min_length 1000; + gzip_comp_level 2; + # text/html is always included by default + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml application/rdf+xml; + gzip_disable "MSIE [1-6]\."; + + ## Transaction service mounting point + location /txs/static { + alias /nginx-txs/staticfiles; + expires 365d; + } + + location /txs/ { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + # we don't want nginx trying to do something clever with + # redirects, we set the Host: header above already. + proxy_redirect off; + proxy_pass http://txs_app_server/; + + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Real-IP $remote_addr; + add_header Front-End-Https on; + } + + ## Config service mounting point + location /cfg/static { + alias /nginx-cfg/staticfiles; + expires 365d; + } + + location /cfg/ { + proxy_pass http://cfg_app_server/; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + add_header Front-End-Https on; + # we don't want nginx trying to do something clever with + # redirects, we set the Host: header above already. + proxy_redirect off; + # They default to 60s. Increase to avoid WORKER TIMEOUT in web container + proxy_connect_timeout 60s; + proxy_read_timeout 60s; + } + + ## Client gateway mounting point + location /cgw/ { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + # we don't want nginx trying to do something clever with + # redirects, we set the Host: header above already. + proxy_redirect off; + proxy_pass http://cgw_app_server/; + + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Real-IP $remote_addr; + add_header Front-End-Https on; + } + } +} \ No newline at end of file diff --git a/fixture/db/accounts.json b/fixture/db/accounts.json new file mode 100644 index 000000000..2de55b9d7 --- /dev/null +++ b/fixture/db/accounts.json @@ -0,0 +1,11 @@ +[ + { + "_id": { "$oid": "60a38b36bf804b033c5e3faa" }, + "active": true, + "email": "peter@thx.network", + "plan": 2, + "createdAt": { "$date": "2021-05-18T09:39:03.446Z" }, + "updatedAt": { "$date": "2021-05-18T09:39:23.128Z" }, + "__v": 0 + } +] diff --git a/fixture/db/client.json b/fixture/db/client.json new file mode 100644 index 000000000..2006cf40f --- /dev/null +++ b/fixture/db/client.json @@ -0,0 +1,163 @@ +[ + { + "_id": "puqOQ6ZT9U8UsfyzuTIwT", + "payload": { + "application_type": "web", + "grant_types": ["authorization_code", "refresh_token"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": ["https://localhost:8082"], + "require_auth_time": false, + "response_types": ["code"], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": ["https://localhost:8082"], + "client_id_issued_at": 1621329421, + "client_id": "puqOQ6ZT9U8UsfyzuTIwT", + "client_name": "THX Dashboard", + "client_secret_expires_at": 0, + "client_secret": "VVrUT7n82KNcuh947RAI-dqY79K8r47k_Xk9WvdKXl_viltV5kzX5oNDjen97vr0pBRSlv3oGZvz3_ka1136LQ", + "redirect_uris": ["https://localhost:8082/signin-oidc", "https://localhost:8082/silent-renew.html"], + "scope": "openid account:read account:write pools:read pools:write erc20:read erc20:write erc721:read erc721:write erc1155:read erc1155:write rewards:read rewards:write withdrawals:read deposits:read deposits:write promotions:read promotions:write widgets:write widgets:read transactions:read members:read members:write payments:read payments:write swaprule:read swaprule:write claims:read brands:read brands:write clients:read clients:write erc20_rewards:read erc20_rewards:write erc721_rewards:read erc721_rewards:write referral_rewards:read referral_rewards:write merchants:write merchants:read webhooks:write webhooks:read custom_rewards:write custom_rewards:read coupon_rewards:write coupon_rewards:read web3_quests:write web3_quests:read discord_role_rewards:write discord_role_rewards:read" + } + }, + { + "_id": "JNd4hvx0TCZvnnXiTCnar", + "payload": { + "application_type": "web", + "grant_types": ["authorization_code", "refresh_token"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": ["https://localhost:8083"], + "require_auth_time": false, + "response_types": ["code"], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": ["http://localhost:8083"], + "client_id_issued_at": 1621329462, + "client_id": "JNd4hvx0TCZvnnXiTCnar", + "client_name": "THX Wallet", + "client_secret_expires_at": 0, + "client_secret": "sefR_erB65hgzp18RCj4OB6ZBCAO-jUba-4M-A8zkaQftBZD2boy-ZSSbGRBHtqXUssSI6TJZVN3W8QIyS34yw", + "redirect_uris": ["https://localhost:8083/signin-oidc", "https://localhost:8083/silent-renew.html"], + "scope": "openid offline_access rewards:read erc20:read erc721:read withdrawals:read withdrawals:write deposits:read deposits:write account:read account:write memberships:read memberships:write promotions:read transactions:read relay:write swap:read swap:write swaprule:read swaprule:write claims:read wallets:read wallets:write" + } + }, + { + "_id": "vdYn6w3z9wQ9TO7gbZkEO", + "payload": { + "application_type": "web", + "grant_types": ["client_credentials"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": [], + "require_auth_time": false, + "response_types": [], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": [], + "client_id_issued_at": 1621329896, + "client_id": "vdYn6w3z9wQ9TO7gbZkEO", + "client_name": "THX CMS", + "client_secret_expires_at": 0, + "client_secret": "gCCe45P1arpSfy9605iXiRno7yp1pmFafbXQMk_MUzE6eRNJHpSK3h63fMI_E9pChBhIBtF1cQBvFxKTCPVfjQ", + "redirect_uris": [], + "scope": "openid metrics:read" + } + }, + { + "_id": "ISsh6Xw6wDISihJS2xs2_", + "payload": { + "application_type": "web", + "grant_types": ["client_credentials"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": [], + "require_auth_time": false, + "response_types": [], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": [], + "client_id_issued_at": 1635194521, + "client_id": "ISsh6Xw6wDISihJS2xs2_", + "client_name": "THX API", + "client_secret_expires_at": 0, + "client_secret": "BnQrAHSb4UDnpR-9klfFAQbZvq_RjVZgLTKwRD3qfOjawX21jrnbBxvSOU84EwqAy1J-fNKvxD2qZen5Gm8jXg", + "redirect_uris": [], + "scope": "openid accounts:read accounts:write" + } + }, + { + "_id": "P4bLsMzQIwWlr_DOhH_qE", + "payload": { + "application_type": "web", + "grant_types": ["client_credentials"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": [], + "require_auth_time": false, + "response_types": [], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": [], + "client_id_issued_at": 1655295083, + "client_id": "P4bLsMzQIwWlr_DOhH_qE", + "client_name": "THX Auth", + "client_secret_expires_at": 0, + "client_secret": "o7RMGeLieULzp0bAiuq6nfukFBCLKxo8N867SzeZNrinshSb15W9NggU93CdtrE_0jTWLUl3U2YkcXYtD3hdcQ", + "redirect_uris": [], + "scope": "openid brands:read claims:write claims:read wallets:write wallets:read pools:write pools:read" + } + }, + { + "_id": "8pqC-D11si73rCKz8bmNV", + "payload": { + "application_type": "web", + "grant_types": ["authorization_code", "refresh_token"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": ["https://localhost:8080/signout-popup.html"], + "require_auth_time": false, + "response_types": ["code"], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": ["https://localhost:8080"], + "client_id_issued_at": 1669134665, + "client_id": "8pqC-D11si73rCKz8bmNV", + "client_name": "THX Widget", + "client_secret_expires_at": 0, + "client_secret": "9rfpbMSwVxEaZUpmsCWVldpO-xy7H13YT5AoMIdFNUX9pWosR3cTfoDyJgnuqFqepqRXbgMhb0PFYwOWgwQoEQ", + "redirect_uris": ["https://localhost:8080/signin-popup.html", "https://localhost:8080/signin-silent.html"], + "scope": "openid offline_access account:read account:write erc20:read erc721:read erc1155:read point_balances:read referral_rewards:read point_rewards:read wallets:read wallets:write pool_subscription:read pool_subscription:write claims:read" + } + }, + { + "_id": "9qqC-D11si12rCKz8bmNV", + "payload": { + "application_type": "web", + "grant_types": ["client_credentials"], + "id_token_signed_response_alg": "RS256", + "post_logout_redirect_uris": ["https://localhost:8080/signout-popup.html"], + "require_auth_time": false, + "response_types": [], + "subject_type": "public", + "token_endpoint_auth_method": "client_secret_basic", + "introspection_endpoint_auth_method": "client_secret_basic", + "require_signed_request_object": false, + "request_uris": ["https://localhost:8080"], + "client_id_issued_at": 1669134665, + "client_id": "9qqC-D11si12rCKz8bmNV", + "client_name": "THX Discord", + "client_secret_expires_at": 0, + "client_secret": "9rfpbMSwVxEaZUpmsCWVldpO-xy7H13YT5AoMIdFNUX9pWosR3cTfoDyJgnuqFqepqRXbgMhb0PFYwOWgwQoEQ", + "redirect_uris": ["https://localhost:8080/signin-popup.html"], + "scope": "openid account:read erc20:read erc721:read point_balances:read referral_rewards:read point_rewards:read wallets:read pools:read" + } + } +] diff --git a/fixture/db/registration_access_token.json b/fixture/db/registration_access_token.json new file mode 100644 index 000000000..d1c170dcd --- /dev/null +++ b/fixture/db/registration_access_token.json @@ -0,0 +1,47 @@ +[ + { + "_id": "CbIkcp42z8bWlNEBJWVckr4Yq6yyxBsoxnOVb2dSdR1", + "payload": { + "iat": 1621329421, + "clientId": "puqOQ6ZT9U8UsfyzuTIwT", + "kind": "RegistrationAccessToken", + "jti": "CbIkcp42z8bWlNEBJWVckr4Yq6yyxBsoxnOVb2dSdR1" + } + }, + { + "_id": "VcsLVLBur1nfu3Ruy3U1Gika9TsBuoHWyb2UMRzAhHg", + "payload": { + "iat": 1621329896, + "clientId": "vdYn6w3z9wQ9TO7gbZkEO", + "kind": "RegistrationAccessToken", + "jti": "VcsLVLBur1nfu3Ruy3U1Gika9TsBuoHWyb2UMRzAhHg" + } + }, + { + "_id": "5oWplA44GmXBdaG94s2v4T55WXVnKzFoXcFRo6Q5x3H", + "payload": { + "iat": 1621329462, + "clientId": "JNd4hvx0TCZvnnXiTCnar", + "kind": "RegistrationAccessToken", + "jti": "5oWplA44GmXBdaG94s2v4T55WXVnKzFoXcFRo6Q5x3H" + } + }, + { + "_id": "TRYjXYjggZXZaqIlygO04U89-3BN2zg2xkQ9ZFiJL5-", + "payload": { + "iat": 1635194521, + "clientId": "ISsh6Xw6wDISihJS2xs2_", + "kind": "RegistrationAccessToken", + "jti": "TRYjXYjggZXZaqIlygO04U89-3BN2zg2xkQ9ZFiJL5-" + } + }, + { + "_id": "8PWFMcB1clE7TcH3_MhDTPrKm4mlzrInqbp7PdhZ226", + "payload": { + "iat": 1669134665, + "clientId": "8pqC-D11si73rCKz8bmNV", + "kind": "RegistrationAccessToken", + "jti": "8PWFMcB1clE7TcH3_MhDTPrKm4mlzrInqbp7PdhZ226" + } + } +] diff --git a/fixture/db/wallets.json b/fixture/db/wallets.json new file mode 100644 index 000000000..8172e4599 --- /dev/null +++ b/fixture/db/wallets.json @@ -0,0 +1,59 @@ +[ + { + "_id": { + "$oid": "63ca9d8a82ba39fc1d15bef5" + }, + "sub": "60a38b36bf804b033c5e3faa", + "chainId": 31337, + "createdAt": { + "$date": { + "$numberLong": "1674222986757" + } + }, + "updatedAt": { + "$date": { + "$numberLong": "1674222986996" + } + }, + "__v": 0, + "address": "0xE1AEC35F626F54De2979C3297B875277D5Cf052B" + }, + { + "_id": { + "$oid": "63ca9d8a82ba39fc1d15befa" + }, + "sub": "60a38b36bf804b033c5e3faa", + "chainId": 137, + "createdAt": { + "$date": { + "$numberLong": "1674222986777" + } + }, + "updatedAt": { + "$date": { + "$numberLong": "1674223002142" + } + }, + "__v": 0, + "address": "0x10af5daFEE2505DD4980D98beCc253d35Bc60684" + }, + { + "_id": { + "$oid": "63ca9d8a82ba39fc1d15bef6" + }, + "sub": "60a38b36bf804b033c5e3faa", + "chainId": 80001, + "createdAt": { + "$date": { + "$numberLong": "1674222986757" + } + }, + "updatedAt": { + "$date": { + "$numberLong": "1674223004075" + } + }, + "__v": 0, + "address": "0xC05f86A0E4aA365E89FB951764462a7874A3B97B" + } +] diff --git a/libs/common/project.json b/libs/common/project.json index 5552be300..e4ef77d86 100644 --- a/libs/common/project.json +++ b/libs/common/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/common/src", "projectType": "library", + "tags": [], "targets": { "build": { "executor": "@nrwl/js:tsc", @@ -21,6 +22,5 @@ "lintFilePatterns": ["libs/common/**/*.ts"] } } - }, - "tags": [] + } } diff --git a/libs/common/src/index.ts b/libs/common/src/index.ts index 27510c8a4..bae13b640 100644 --- a/libs/common/src/index.ts +++ b/libs/common/src/index.ts @@ -1,4 +1,5 @@ import { Sentry } from './lib/sentry'; import { track } from './lib/mixpanel'; +import { migrateMongoScript } from './lib/migrate-mongo'; -export { Sentry, track }; +export { Sentry, track, migrateMongoScript }; diff --git a/libs/common/src/lib/chains.ts b/libs/common/src/lib/chains.ts new file mode 100644 index 000000000..ec46f1bb8 --- /dev/null +++ b/libs/common/src/lib/chains.ts @@ -0,0 +1,61 @@ +import { ChainId } from './enums'; + +type ChainInfo = { name: string; chainId: number; blockExplorer: string; rpc: string }; + +const chainList: { [chainId: number]: ChainInfo } = { + [ChainId.Ethereum]: { + chainId: ChainId.Ethereum, + name: 'Ethereum', + blockExplorer: 'https://etherscan.com', + rpc: 'https://cloudflare-eth.com', + }, + [ChainId.BNBChain]: { + chainId: ChainId.BNBChain, + name: 'BNB Chain', + blockExplorer: 'https://bscscan.com', + rpc: 'https://rpc.ankr.com/bsc', + }, + [ChainId.Arbitrum]: { + chainId: ChainId.Arbitrum, + name: 'Arbitrum', + blockExplorer: 'https://arbiscan.io', + rpc: 'https://arb1.arbitrum.io/rpc', + }, + [ChainId.Polygon]: { + chainId: ChainId.Polygon, + name: 'Polygon', + blockExplorer: 'https://polygonscan.com', + rpc: 'https://polygon-rpc.com', + }, + [ChainId.PolygonZK]: { + chainId: ChainId.PolygonZK, + name: 'Polygon zkEVM', + blockExplorer: 'https://zkevm.polygonscan.com', + rpc: 'https://zkevm-rpc.com', + }, + [ChainId.Linea]: { + chainId: ChainId.Linea, + name: 'Linea', + blockExplorer: 'https://lineascan.build', + rpc: 'https://rpc.linea.build', + }, +}; + +if (process.env['NODE_ENV'] !== 'production') { + chainList[ChainId.Hardhat] = { + chainId: ChainId.Hardhat, + name: 'Hardhat', + blockExplorer: 'https://hardhatscan.com', + rpc: 'http://127.0.0.1:8545', + }; +} + +function getTokenURL(chainId: ChainId, address: string) { + return `${chainList[chainId].blockExplorer}/token/${address}`; +} + +function getAddressURL(chainId: ChainId, address: string) { + return `${chainList[chainId].blockExplorer}/address/${address}`; +} + +export { chainList, getTokenURL, getAddressURL }; diff --git a/libs/common/src/lib/constants.ts b/libs/common/src/lib/constants.ts new file mode 100644 index 000000000..b2b72a0d0 --- /dev/null +++ b/libs/common/src/lib/constants.ts @@ -0,0 +1,250 @@ +import { AccountPlanType, Goal, Role } from './enums'; + +export const planPricingMap = { + [AccountPlanType.Lite]: { + subscriptionLimit: 100, + costSubscription: 1900, + costPerUnit: 8, + }, + [AccountPlanType.Premium]: { + subscriptionLimit: 5000, + costSubscription: 48900, + costPerUnit: 5, + }, +}; + +export const GITHUB_API_ENDPOINT = 'https://api.github.com'; +export const TWITTER_API_ENDPOINT = 'https://api.twitter.com/2'; +export const GOOGLE_API_ENDPOINT = 'https://www.googleapis.com'; +export const DISCORD_API_ENDPOINT = 'https://discord.com/api/v10'; +export const TWITCH_API_ENDPOINT = 'https://api.twitch.tv/helix'; +export const DEFAULT_ELEMENTS = { + btnBg: { + label: 'Button', + color: '#5942c1', + }, + btnText: { + label: 'Button Text', + color: '#FFFFFF', + }, + text: { + label: 'Text', + color: '#FFFFFF', + }, + bodyBg: { + label: 'Background', + color: '#241956', + }, + cardBg: { + label: 'Card', + color: '#31236d', + }, + cardText: { + label: 'Card Text', + color: '#FFFFFF', + }, + navbarBg: { + label: 'Navigation', + color: '#31236d', + }, + navbarBtnBg: { + label: 'Navigation Button', + color: '#5942c1', + }, + navbarBtnText: { + label: 'Navigation Button Text', + color: '#FFFFFF', + }, + launcherBg: { + label: 'Launcher', + color: '#5942c1', + }, + launcherIcon: { + label: 'Launcher Icon', + color: '#ffffff', + }, +}; + +export const DEFAULT_COLORS = { + accent: { + label: 'Accent', + color: '#98D80D', + }, + success: { + label: 'Success', + color: '#28a745', + }, + warning: { + label: 'Warning', + color: '#ffe500', + }, + danger: { + label: 'Danger', + color: '#dc3545', + }, + info: { + label: 'Info', + color: '#17a2b8', + }, +}; + +export const roleLabelMap = { + [Role.None]: 'Select a role', + [Role.GrowthHacker]: 'Growth Hacker', + [Role.Marketer]: 'Marketer', + [Role.CommunityManager]: 'Community Manager', + [Role.Developer]: 'Developer', + [Role.Other]: 'Other', +}; + +export const goalLabelMap = { + [Goal.Reward]: 'Reward users in my game or app', + [Goal.Retain]: 'Retain players or members', + [Goal.Referral]: 'Set up referrals', + [Goal.Social]: 'Integrate rewards in social channels', + [Goal.Mint]: 'Mint tokens', +}; + +export const contentRewards = { + 'coin-reward': { + tag: 'Coin Reward', + title: 'Cashbacks with Coins', + description: 'Import your own ERC20 smart contract and let users redeem points for coins.', + list: ['Provide tangible value', 'Boost user retention', 'Incentivize spending'], + docsUrl: 'https://docs.thx.network/rewards/coins', + icon: 'fas fa-coins', // Suggested icon for coins + color: '#FFD700', // Suggested color for coins (Gold) + }, + 'nft-reward': { + tag: 'NFT Reward', + title: 'Exclusive NFTs', + description: 'Import your own ERC721 or ERC1155 smart contract and let users redeem points for exclusive NFTs.', + list: ['Offer unique collectibles', 'Enhance user engagement', 'Drive interest in NFTs'], + docsUrl: 'https://docs.thx.network/rewards/nft', + icon: 'fas fa-gem', // Suggested icon for gems or precious items + color: '#9370DB', // Suggested color for NFTs (Light Purple) + }, + 'custom-reward': { + tag: 'Custom Reward', + title: 'Flexible Rewards', + description: 'Use inbound webhooks to reward users with custom features in your application.', + list: ['Tailor rewards to user needs', 'Enhance user satisfaction', 'Drive app adoption'], + docsUrl: 'https://docs.thx.network/rewards', + icon: 'fas fa-cogs', // Suggested icon for customization or settings + color: '#808080', // Suggested color for customization (Gray) + }, + 'discord-role-reward': { + tag: 'Discord Role Reward', + title: 'Exclusive Discord Roles', + description: + 'Grant users the ability to redeem points for exclusive Discord roles within your community server.', + list: ['Promote community status', 'Encourage active participation', 'Facilitate social interaction'], + docsUrl: 'https://docs.thx.network/rewards/discord-role', + icon: 'fab fa-discord', // Suggested icon for shield or protection + color: '#7289DA', // Suggested color for Discord roles (Discord Blue) + }, + 'qr-codes': { + tag: 'QR Codes', + title: 'Offline Reward Distribution', + description: 'Use QR codes to distribute rewards in offline environments.', + list: ['Expand reach to offline users', 'Facilitate in-person engagement', 'Enhance brand recognition'], + docsUrl: 'https://docs.thx.network', + icon: 'fas fa-qrcode', // Suggested icon for QR codes + color: '#000000', // Suggested color for QR codes (Black) + }, +}; + +export const contentQuests = { + 'steam-quest': { + tag: 'Steam Quest', + icon: 'fab fa-steam', + title: 'Unlock Steam engagement', + description: 'Embark on a gaming journey by purchasing, wishlisting games, and earning Steam achievements.', + list: ['Buy a game on Steam', 'Wishlist a game on Steam', 'Earn a Steam achievement'], + docsUrl: 'https://docs.thx.network/user-guides/quests/daily-quests', + color: '#171d25', + }, + 'twitter-quest': { + tag: 'Twitter Quest', + icon: 'fab fa-twitter', + title: 'Boost your Twitter presence', + description: 'Engage your audience on Twitter by creating exciting quests that encourage retweets and likes.', + list: ['Increase followers', 'Enhance brand recognition', 'Foster community engagement'], + docsUrl: 'https://docs.thx.network/user-guides/quests/social-quests', + color: '#1B95E0', + }, + 'daily-quest': { + tag: 'Daily Quest', + title: 'Boost user engagement', + icon: 'fas fa-calendar', + description: 'Provide daily incentives for returning to your website.', + list: ['Encourage regular visits', 'Enhance user loyalty', 'Foster community growth'], + docsUrl: 'https://docs.thx.network/user-guides/quests/daily-quests', + color: '#4CAF50', + }, + 'custom-quest': { + tag: 'Custom Quest', + icon: 'fas fa-trophy', + title: 'Seamless integration', + description: 'Integrate quests with ease using webhooks to reward important achievements in your application.', + list: ['Tailor rewards to your app', 'Streamline integration', 'Enhance user experience'], + docsUrl: 'https://docs.thx.network/user-guides/quests/custom-quests', + color: '#9370DB', + }, + 'youtube-quest': { + tag: 'Youtube Quest', + icon: 'fab fa-youtube', + title: 'Expand your YouTube presence', + description: + 'Amplify your presence on YouTube by creating quests that encourage likes, shares, and subscriptions.', + list: ['Increase video views', 'Boost channel subscribers', 'Enhance YouTube community engagement'], + docsUrl: 'https://docs.thx.network/user-guides/quests/social-quests', + color: '#FF0000', + }, + 'invite-quest': { + tag: 'Invite Quest', + icon: 'fas fa-comments', + title: 'Drive user acquisition', + description: 'Empower your players to earn rewards for referrals.', + list: ['Expand your user base', 'Lower acquisition costs', 'Strengthen player networks'], + docsUrl: 'https://docs.thx.network/user-guides/quests/referral-quests', + color: '#FFA500', + }, + 'discord-quest': { + tag: 'Discord Quest', + icon: 'fab fa-discord', + title: 'Strengthen your Discord community', + description: + 'Create quests on Discord to promote community interactions and build a strong, engaged user base.', + list: ['Grow your Discord server', 'Enhance community participation', 'Boost server activity'], + docsUrl: 'https://docs.thx.network/user-guides/quests', + color: '#5865F2', + }, + 'web3-quest': { + tag: 'Web3 Quest', + icon: 'fab fa-ethereum', + title: 'Empower with Web3 rewards', + description: "Reward users' coin balance or NFT ownership using smart contracts.", + list: ['Leverage blockchain technology', 'Enhance user ownership', 'Facilitate decentralized rewards'], + docsUrl: 'https://docs.thx.network/user-guides/quests', + color: '#3C3C3D', + }, + 'gitcoin-quest': { + tag: 'Gitcoin Quest', + icon: 'fas fa-fingerprint', + title: 'Use Gitcoin Passport for sybil resistance', + description: 'Use this quest to verify if wallets are owned by real humans.', + list: ['Increase Sybil Resistance', 'Gitcoin Unique Humanity Scorer', 'Tap into new ecosystems'], + docsUrl: 'https://docs.thx.network/user-guides/quests', + color: '#3498db', + }, + 'webhook-quest': { + tag: 'Webhook Quest', + icon: 'fas fa-globe', + title: 'Use your own API endpoints', + description: 'Use your API endpoint to validate quests for account identities.', + list: ['Track real engagement', 'Your own validation method', 'Account identities'], + docsUrl: 'https://docs.thx.network/user-guides/quests', + color: '#3498db', + }, +}; diff --git a/libs/common/src/lib/enums/AccessTokenKind.ts b/libs/common/src/lib/enums/AccessTokenKind.ts new file mode 100644 index 000000000..7c0110e65 --- /dev/null +++ b/libs/common/src/lib/enums/AccessTokenKind.ts @@ -0,0 +1,72 @@ +export enum AccessTokenKind { + Auth = 'authentication', + Signup = 'signup', + VerifyEmail = 'verify-email', + Google = 'google', + Twitter = 'twitter', + Discord = 'discord', + Twitch = 'twitch', + Github = 'github', +} + +export enum OAuthGoogleScope { + OpenID = 'openid', + Email = 'https://www.googleapis.com/auth/userinfo.email', + YoutubeReadOnly = 'https://www.googleapis.com/auth/youtube.readonly', +} + +export enum OAuthTwitterScope { + OfflineAccess = 'offline.access', + UsersRead = 'users.read', + TweetRead = 'tweet.read', + FollowsWrite = 'follows.write', + LikeRead = 'like.read', +} + +export enum OAuthDiscordScope { + Identify = 'identify', + Email = 'email', + Guilds = 'guilds', +} + +export enum OAuthTwitchScope { + Email = 'user:read:email', + Follows = 'user:read:follows', + Broadcast = 'user:read:broadcast', +} + +export enum OAuthGithubScope { + PublicRepo = 'public_repo', +} + +// Different scope requirements should always be elevating based on the invasiveness of the required scope +// This allows for better privacy by design +export const OAuthRequiredScopes = { + GoogleAuth: [OAuthGoogleScope.OpenID, OAuthGoogleScope.Email], + GoogleYoutubeSubscribe: [OAuthGoogleScope.OpenID, OAuthGoogleScope.Email, OAuthGoogleScope.YoutubeReadOnly], + GoogleYoutubeLike: [OAuthGoogleScope.OpenID, OAuthGoogleScope.Email, OAuthGoogleScope.YoutubeReadOnly], + TwitterAuth: [OAuthTwitterScope.OfflineAccess, OAuthTwitterScope.UsersRead, OAuthTwitterScope.TweetRead], + TwitterValidateUser: [OAuthTwitterScope.OfflineAccess, OAuthTwitterScope.UsersRead, OAuthTwitterScope.TweetRead], + TwitterValidateRepost: [OAuthTwitterScope.OfflineAccess, OAuthTwitterScope.UsersRead, OAuthTwitterScope.TweetRead], + TwitterValidateMessage: [OAuthTwitterScope.OfflineAccess, OAuthTwitterScope.UsersRead, OAuthTwitterScope.TweetRead], + TwitterValidateLike: [ + OAuthTwitterScope.OfflineAccess, + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + OAuthTwitterScope.LikeRead, + ], + TwitterValidateFollow: [ + OAuthTwitterScope.OfflineAccess, + OAuthTwitterScope.UsersRead, + OAuthTwitterScope.TweetRead, + OAuthTwitterScope.LikeRead, + OAuthTwitterScope.FollowsWrite, + ], + TwitterAutoQuest: [OAuthTwitterScope.OfflineAccess, OAuthTwitterScope.UsersRead, OAuthTwitterScope.TweetRead], + DiscordAuth: [OAuthDiscordScope.Identify, OAuthDiscordScope.Email], + DiscordValidateGuild: [OAuthDiscordScope.Identify, OAuthDiscordScope.Email, OAuthDiscordScope.Guilds], + TwitchAuth: [OAuthTwitchScope.Email, OAuthTwitchScope.Follows, OAuthTwitchScope.Broadcast], + GithubAuth: [OAuthGithubScope.PublicRepo], +}; + +export type OAuthScope = OAuthGoogleScope | OAuthTwitterScope | OAuthDiscordScope | OAuthTwitchScope | OAuthGithubScope; diff --git a/libs/common/src/lib/enums/AccountPlanType.ts b/libs/common/src/lib/enums/AccountPlanType.ts new file mode 100644 index 000000000..a33e94817 --- /dev/null +++ b/libs/common/src/lib/enums/AccountPlanType.ts @@ -0,0 +1,4 @@ +export enum AccountPlanType { + Lite = 0, + Premium = 1, +} diff --git a/libs/common/src/lib/enums/AccountVariant.ts b/libs/common/src/lib/enums/AccountVariant.ts new file mode 100644 index 000000000..059a9b7d8 --- /dev/null +++ b/libs/common/src/lib/enums/AccountVariant.ts @@ -0,0 +1,18 @@ +export enum AccountVariant { + EmailPassword = 0, + SSOGoogle = 1, + SSOTwitter = 2, + SSOSpotify = 3, // @dev Deprecated + Metamask = 4, + SSOGithub = 5, + SSODiscord = 6, + SSOTwitch = 7, +} + +export enum ReCaptchaAction { + QuestDailyEntryCreate = 'QUEST_DAILY_ENTRY_CREATE', + QuestSocialEntryCreate = 'QUEST_SOCIAL_ENTRY_CREATE', + QuestCustomEntryCreate = 'QUEST_CUSTOM_ENTRY_CREATE', + QuestWeb3EntryCreate = 'QUEST_WEB3_ENTRY_CREATE', + QuestGitcoinEntryCreate = 'QUEST_GITCOIN_ENTRY_CREATE', +} diff --git a/libs/common/src/lib/enums/ChainId.ts b/libs/common/src/lib/enums/ChainId.ts new file mode 100644 index 000000000..aa8f31b14 --- /dev/null +++ b/libs/common/src/lib/enums/ChainId.ts @@ -0,0 +1,9 @@ +export enum ChainId { + Ethereum = 1, + Arbitrum = 42161, + BNBChain = 56, + Hardhat = 31337, + Polygon = 137, + PolygonZK = 1101, + Linea = 59144, +} diff --git a/libs/common/src/lib/enums/Collaborator.ts b/libs/common/src/lib/enums/Collaborator.ts new file mode 100644 index 000000000..8bc837427 --- /dev/null +++ b/libs/common/src/lib/enums/Collaborator.ts @@ -0,0 +1,5 @@ +export enum CollaboratorInviteState { + Pending = 0, + Accepted = 1, + Expired = 2, +} diff --git a/libs/common/src/lib/enums/DailyRewardClaimState.ts b/libs/common/src/lib/enums/DailyRewardClaimState.ts new file mode 100644 index 000000000..d76454f67 --- /dev/null +++ b/libs/common/src/lib/enums/DailyRewardClaimState.ts @@ -0,0 +1,4 @@ +export enum DailyRewardClaimState { + Pending = 0, + Claimed = 1, +} diff --git a/libs/common/src/lib/enums/ERC1155.ts b/libs/common/src/lib/enums/ERC1155.ts new file mode 100644 index 000000000..1fec73de7 --- /dev/null +++ b/libs/common/src/lib/enums/ERC1155.ts @@ -0,0 +1,7 @@ +export enum ERC1155TokenState { + Pending = 0, + Failed = 1, + Minted = 2, + Transferring = 3, + Transferred = 4, +} diff --git a/libs/common/src/lib/enums/ERC20Type.ts b/libs/common/src/lib/enums/ERC20Type.ts new file mode 100644 index 000000000..b30072301 --- /dev/null +++ b/libs/common/src/lib/enums/ERC20Type.ts @@ -0,0 +1,5 @@ +export enum ERC20Type { + Unknown = -1, + Limited = 0, + Unlimited = 1, +} diff --git a/libs/common/src/lib/enums/ERC721Variant.ts b/libs/common/src/lib/enums/ERC721Variant.ts new file mode 100644 index 000000000..0b8d890fa --- /dev/null +++ b/libs/common/src/lib/enums/ERC721Variant.ts @@ -0,0 +1,14 @@ +export enum ERC721Variant { + Unknown = -1, + Default = 0, + OpenSea = 1, + Lottery = 2, +} + +export enum ERC721TokenState { + Pending = 0, + Failed = 1, + Minted = 2, + Transferring = 3, + Transferred = 4, +} diff --git a/libs/common/src/lib/enums/Event.ts b/libs/common/src/lib/enums/Event.ts new file mode 100644 index 000000000..d4f4baf70 --- /dev/null +++ b/libs/common/src/lib/enums/Event.ts @@ -0,0 +1,9 @@ +export enum Event { + QuestDailyComplete = 'quest_daily.complete', + QuestInviteComplete = 'quest_invite.complete', + QuestSocialComplete = 'quest_social.complete', + QuestCustomComplete = 'quest_custom.complete', + RewardCoinPayment = 'reward_coin.paid', + RewardNFTPayment = 'reward_nft.paid', + RewardCustomPayment = 'reward_custom.paid', +} diff --git a/libs/common/src/lib/enums/GateVariant.ts b/libs/common/src/lib/enums/GateVariant.ts new file mode 100644 index 000000000..502bd5b9b --- /dev/null +++ b/libs/common/src/lib/enums/GateVariant.ts @@ -0,0 +1,6 @@ +export enum GateVariant { + ERC721 = 0, + ERC1155 = 1, + ERC20 = 2, + UniqueHumanity = 3, +} diff --git a/libs/common/src/lib/enums/GrantVariant.ts b/libs/common/src/lib/enums/GrantVariant.ts new file mode 100644 index 000000000..062c7184a --- /dev/null +++ b/libs/common/src/lib/enums/GrantVariant.ts @@ -0,0 +1,4 @@ +export enum GrantVariant { + AuthorizationCode = 'authorization_code', + ClientCredentials = 'client_credentials', +} diff --git a/libs/common/src/lib/enums/Job.ts b/libs/common/src/lib/enums/Job.ts new file mode 100644 index 000000000..e953817d8 --- /dev/null +++ b/libs/common/src/lib/enums/Job.ts @@ -0,0 +1,19 @@ +export enum JobType { + UpdateParticipantRanks = 'updateParticipantRanks', + UpdateCampaignRanks = 'updateCampaignRanks', + UpdatePendingTransactions = 'updatePendingTransactions', + CreateTwitterQuests = 'createTwitterQuests', + SendCampaignReport = 'sendPoolAnalyticsReport', + MigrateWallets = 'migrateWallets', + DeploySafe = 'deploySafe', + CreateQuestEntry = 'createQuestEntry', + CreateRewardPayment = 'createRewardPayment', + RequestAttemp = 'requestAttempt', + UpdateTwitterLikeCache = 'updateTwitterLikeCache', + UpdateTwitterRepostCache = 'updateTwitterRepostCache', + UpsertInvoices = 'upsertInvoices', + UpdatePrices = 'updatePrices', + UpdateAPR = 'updateAPR', + AssertPayments = 'assertPayments', + ClaimExternalRewards = 'claimExternalRewards', +} diff --git a/libs/common/src/lib/enums/NFTVariant.ts b/libs/common/src/lib/enums/NFTVariant.ts new file mode 100644 index 000000000..e87cfec3e --- /dev/null +++ b/libs/common/src/lib/enums/NFTVariant.ts @@ -0,0 +1,4 @@ +export enum NFTVariant { + ERC721 = 'erc721', + ERC1155 = 'erc1155', +} diff --git a/libs/common/src/lib/enums/PlatformVariant.ts b/libs/common/src/lib/enums/PlatformVariant.ts new file mode 100644 index 000000000..6fda9bde0 --- /dev/null +++ b/libs/common/src/lib/enums/PlatformVariant.ts @@ -0,0 +1,7 @@ +export enum PlatformVariant { + None = 0, + Google = 1, + Twitter = 2, + Spotify = 3, + Github = 4, +} diff --git a/libs/common/src/lib/enums/QuestSocialRequirement.ts b/libs/common/src/lib/enums/QuestSocialRequirement.ts new file mode 100644 index 000000000..29cd3aa69 --- /dev/null +++ b/libs/common/src/lib/enums/QuestSocialRequirement.ts @@ -0,0 +1,13 @@ +export enum QuestSocialRequirement { + YouTubeLike = 0, + YouTubeSubscribe = 1, + TwitterLike = 2, + TwitterRetweet = 3, + TwitterFollow = 4, + DiscordGuildJoined = 5, + TwitterQuery = 6, + TwitterLikeRetweet = 7, + DiscordMessage = 8, + DiscordMessageReaction = 9, + DiscordGuildRole = 10, +} diff --git a/libs/common/src/lib/enums/RewardConditionPlatform.ts b/libs/common/src/lib/enums/RewardConditionPlatform.ts new file mode 100644 index 000000000..8d23b03ff --- /dev/null +++ b/libs/common/src/lib/enums/RewardConditionPlatform.ts @@ -0,0 +1,10 @@ +// export enum RewardConditionPlatform { +// None = 0, +// Google = 1, +// Twitter = 2, +// Spotify = 3, +// Github = 4, +// Discord = 5, +// Twitch = 6, +// Shopify = 7, +// } diff --git a/libs/common/src/lib/enums/RewardVariant.ts b/libs/common/src/lib/enums/RewardVariant.ts new file mode 100644 index 000000000..cc6ee7a8d --- /dev/null +++ b/libs/common/src/lib/enums/RewardVariant.ts @@ -0,0 +1,20 @@ +export enum RewardVariant { + Coin = 0, + NFT = 1, + Custom = 2, + Coupon = 3, + DiscordRole = 4, + Galachain = 5, +} + +export enum QuestVariant { + Daily = 0, + Invite = 1, + Twitter = 2, + Discord = 3, + YouTube = 4, + Custom = 5, + Web3 = 6, + Gitcoin = 7, + Webhook = 8, +} diff --git a/libs/common/src/lib/enums/Signup.ts b/libs/common/src/lib/enums/Signup.ts new file mode 100644 index 000000000..aa565fd55 --- /dev/null +++ b/libs/common/src/lib/enums/Signup.ts @@ -0,0 +1,16 @@ +export enum Role { + None = 'none', + GrowthHacker = 'growth_hacker', + Marketer = 'marketer', + CommunityManager = 'community_manager', + Developer = 'developer', + Other = 'other', +} + +export enum Goal { + Reward = 'reward', + Retain = 'retain', + Referral = 'referral', + Social = 'social', + Mint = 'mint', +} diff --git a/libs/common/src/lib/enums/TransactionState.ts b/libs/common/src/lib/enums/TransactionState.ts new file mode 100644 index 000000000..c67614496 --- /dev/null +++ b/libs/common/src/lib/enums/TransactionState.ts @@ -0,0 +1,7 @@ +export enum TransactionState { + Queued = 0, + Mined = 1, + Failed = 2, + Sent = 3, + Confirmed = 4, +} diff --git a/libs/common/src/lib/enums/TransactionType.ts b/libs/common/src/lib/enums/TransactionType.ts new file mode 100644 index 000000000..fd3f02700 --- /dev/null +++ b/libs/common/src/lib/enums/TransactionType.ts @@ -0,0 +1,4 @@ +export enum TransactionType { + Default = 0, + Relayed = 1, +} diff --git a/libs/common/src/lib/enums/Wallet.ts b/libs/common/src/lib/enums/Wallet.ts new file mode 100644 index 000000000..4e151849c --- /dev/null +++ b/libs/common/src/lib/enums/Wallet.ts @@ -0,0 +1,4 @@ +export enum WalletVariant { + Safe = 'safe', + WalletConnect = 'walletconnect', +} diff --git a/libs/common/src/lib/enums/Webhook.ts b/libs/common/src/lib/enums/Webhook.ts new file mode 100644 index 000000000..532e3359c --- /dev/null +++ b/libs/common/src/lib/enums/Webhook.ts @@ -0,0 +1,16 @@ +export enum WebhookVariant { + Inbound = 0, + Outbound = 1, +} + +export enum WebhookStatus { + Inactive = 0, + Active = 1, +} + +export enum WebhookRequestState { + Pending = 0, + Sent = 1, + Received = 2, + Failed = 3, +} diff --git a/libs/common/src/lib/enums/index.ts b/libs/common/src/lib/enums/index.ts new file mode 100644 index 000000000..c7df43502 --- /dev/null +++ b/libs/common/src/lib/enums/index.ts @@ -0,0 +1,22 @@ +export * from './AccessTokenKind'; +export * from './AccountPlanType'; +export * from './AccountVariant'; +export * from './ChainId'; +export * from './ERC1155'; +export * from './ERC20Type'; +export * from './ERC721Variant'; +export * from './PlatformVariant'; +export * from './QuestSocialRequirement'; +export * from './GrantVariant'; +export * from './RewardVariant'; +export * from './TransactionState'; +export * from './TransactionType'; +export * from './DailyRewardClaimState'; +export * from './NFTVariant'; +export * from './Signup'; +export * from './GateVariant'; +export * from './Webhook'; +export * from './Event'; +export * from './Job'; +export * from './Collaborator'; +export * from './Wallet'; diff --git a/libs/common/src/lib/index.ts b/libs/common/src/lib/index.ts new file mode 100644 index 000000000..c80fc7150 --- /dev/null +++ b/libs/common/src/lib/index.ts @@ -0,0 +1,8 @@ +export * from './enums'; +export * from './maps'; +export * from './chains'; +export * from './sentry'; +export * from './mixpanel'; +export * from './constants'; +export * from './mail'; +export * from './twitter'; diff --git a/libs/common/src/lib/mail.ts b/libs/common/src/lib/mail.ts new file mode 100644 index 000000000..c850d4ca0 --- /dev/null +++ b/libs/common/src/lib/mail.ts @@ -0,0 +1,29 @@ +import { SES } from '@aws-sdk/client-ses'; + +const ses = new SES({ + region: 'eu-west-3', +}); + +function sendMail(to: string, subject: string, html: string) { + ses.sendEmail( + { + Destination: { ToAddresses: [to] }, + Message: { + Body: { + Html: { + Charset: 'UTF-8', + Data: html, + }, + }, + Subject: { + Charset: 'UTF-8', + Data: subject, + }, + }, + Source: 'THX Network <noreply@thx.network>', + }, + console.log, + ); +} + +export { ses, sendMail }; diff --git a/libs/common/src/lib/maps/index.ts b/libs/common/src/lib/maps/index.ts new file mode 100644 index 000000000..403397e0b --- /dev/null +++ b/libs/common/src/lib/maps/index.ts @@ -0,0 +1,2 @@ +export * from './quest'; +export * from './oauth'; diff --git a/libs/common/src/lib/maps/oauth.ts b/libs/common/src/lib/maps/oauth.ts new file mode 100644 index 000000000..eb21ca764 --- /dev/null +++ b/libs/common/src/lib/maps/oauth.ts @@ -0,0 +1,39 @@ +import { AccountVariant, AccessTokenKind, QuestSocialRequirement } from '../enums'; + +export const providerIconMap = { + [AccessTokenKind.Google]: 'fab fa-youtube', + [AccessTokenKind.Twitter]: 'fab fa-twitter', + [AccessTokenKind.Discord]: 'fab fa-discord', + [AccessTokenKind.Twitch]: 'fab fa-twitch', + [AccessTokenKind.Github]: 'fab fa-github', +}; + +export const accountVariantProviderMap = { + [AccountVariant.SSOGoogle]: AccessTokenKind.Google, + [AccountVariant.SSODiscord]: AccessTokenKind.Discord, + [AccountVariant.SSOTwitter]: AccessTokenKind.Twitter, + [AccountVariant.SSOGithub]: AccessTokenKind.Github, + [AccountVariant.SSOTwitch]: AccessTokenKind.Twitch, +}; + +export const providerAccountVariantMap = { + [AccessTokenKind.Google]: AccountVariant.SSOGoogle, + [AccessTokenKind.Discord]: AccountVariant.SSODiscord, + [AccessTokenKind.Twitter]: AccountVariant.SSOTwitter, + [AccessTokenKind.Github]: AccountVariant.SSOGithub, + [AccessTokenKind.Twitch]: AccountVariant.SSOTwitch, +}; + +export const interactionComponentMap = { + [QuestSocialRequirement.YouTubeSubscribe]: 'BaseDropdownYoutubeChannels', + [QuestSocialRequirement.YouTubeLike]: 'BaseDropdownYoutubeVideo', + [QuestSocialRequirement.TwitterLike]: 'BaseDropdownTwitterTweets', + [QuestSocialRequirement.TwitterRetweet]: 'BaseDropdownTwitterTweets', + [QuestSocialRequirement.TwitterLikeRetweet]: 'BaseDropdownTwitterTweets', + [QuestSocialRequirement.TwitterFollow]: 'BaseDropdownTwitterUsers', + [QuestSocialRequirement.TwitterQuery]: 'BaseDropdownTwitterQuery', + [QuestSocialRequirement.DiscordGuildJoined]: 'BaseDropdownDiscordGuilds', + [QuestSocialRequirement.DiscordGuildRole]: 'BaseDropdownDiscordRoles', + [QuestSocialRequirement.DiscordMessage]: 'BaseDropdownDiscordMessage', + [QuestSocialRequirement.DiscordMessageReaction]: 'BaseDropdownDiscordMessageReaction', +}; diff --git a/libs/common/src/lib/maps/quest.ts b/libs/common/src/lib/maps/quest.ts new file mode 100644 index 000000000..824b20196 --- /dev/null +++ b/libs/common/src/lib/maps/quest.ts @@ -0,0 +1,15 @@ +import { QuestVariant, QuestSocialRequirement } from '../enums'; + +export const questInteractionVariantMap = { + [QuestSocialRequirement.TwitterFollow]: QuestVariant.Twitter, + [QuestSocialRequirement.TwitterLike]: QuestVariant.Twitter, + [QuestSocialRequirement.TwitterQuery]: QuestVariant.Twitter, + [QuestSocialRequirement.TwitterRetweet]: QuestVariant.Twitter, + [QuestSocialRequirement.TwitterLikeRetweet]: QuestVariant.Twitter, + [QuestSocialRequirement.YouTubeLike]: QuestVariant.YouTube, + [QuestSocialRequirement.YouTubeSubscribe]: QuestVariant.YouTube, + [QuestSocialRequirement.DiscordGuildJoined]: QuestVariant.Discord, + [QuestSocialRequirement.DiscordGuildRole]: QuestVariant.Discord, + [QuestSocialRequirement.DiscordMessage]: QuestVariant.Discord, + [QuestSocialRequirement.DiscordMessageReaction]: QuestVariant.Discord, +}; diff --git a/libs/common/src/lib/migrate-mongo.ts b/libs/common/src/lib/migrate-mongo.ts new file mode 100644 index 000000000..55a257ea6 --- /dev/null +++ b/libs/common/src/lib/migrate-mongo.ts @@ -0,0 +1,54 @@ +import program from 'commander'; +import migrateMongo, { config } from 'migrate-mongo'; + +export const migrateMongoScript = (migrateMongoConfig: config.Config) => { + function printMigrated(migrated: string[] = []) { + migrated.forEach((migratedItem) => { + console.log(`MIGRATED UP: ${migratedItem}`); + }); + console.log('DB UP TO DATE'); + } + + function handleError(err: Error) { + console.error(`ERROR: ${err.message}`, err.stack); + process.exit(1); + } + + migrateMongo.config.set(migrateMongoConfig); + + program + .command('up') + .description('run all pending database migrations') + .action(() => { + migrateMongo.database + .connect() + .then(({ db, client }) => migrateMongo.up(db, client)) + .then((migrated) => { + printMigrated(migrated); + process.exit(0); + }) + .catch((err) => { + handleError(err); + printMigrated(err.migrated); + }); + }); + + program + .command('down') + .description('undo the last applied database migration') + .action(() => { + migrateMongo.database + .connect() + .then(({ db, client }) => migrateMongo.down(db, client)) + .then((migrated) => { + migrated.forEach((migratedItem) => { + console.log(`MIGRATED DOWN: ${migratedItem}`); + }); + process.exit(0); + }) + .catch((err) => { + handleError(err); + }); + }); + program.parse(process.argv); +}; diff --git a/libs/common/src/lib/scss/_alert.scss b/libs/common/src/lib/scss/_alert.scss new file mode 100644 index 000000000..2b799b2f1 --- /dev/null +++ b/libs/common/src/lib/scss/_alert.scss @@ -0,0 +1,9 @@ +.alert-top { + margin: 0; + text-align: center; + border-radius: 0; + + a:hover { + text-decoration: none; + } +} diff --git a/libs/common/src/lib/scss/_buttons.scss b/libs/common/src/lib/scss/_buttons.scss new file mode 100644 index 000000000..58ddc896d --- /dev/null +++ b/libs/common/src/lib/scss/_buttons.scss @@ -0,0 +1,112 @@ +[class*='btn-outline-'] { + border-width: 3px; +} + +.btn { + font-family: $font-family-sans-serif; + font-weight: $font-weight-normal !important; + + i { + font-size: 0.8rem; + margin-left: 0.5rem; + } +} + +.btn-chat { + position: fixed; + width: auto; + font-size: 1.2rem; + bottom: 1rem; + right: 1rem; +} + +.btn-nav { + display: block; + color: #000; + font-size: 18px; + font-weight: 700; + text-transform: uppercase; + margin: 0; + position: relative; + + &:focus { + box-shadow: none; + } + + i { + margin-left: 0.5rem; + font-size: $font-size-sm; + } +} + +.btn-sm { + font-size: 14px !important; +} + +@media (max-width: 768px) { + .btn-nav { + margin: auto; + color: black !important; + } + .header-menu--right .btn-primary { + margin-top: 1rem; + } +} + +.btn-remove { + height: 31px; + width: 31px; + background-color: transparent; + color: $gray-400; + border: 1px solid $gray-400; + + &:hover, + &:focus, + &:focus:hover { + background-color: transparent; + color: $danger; + border-color: $danger; + } + + i { + margin: 0; + } +} + +.btn-tab { + flex: 1; + color: white; + background-color: $primary; + margin: 0 1rem; + background-size: contain; + + img { + height: 20px; + } + + @media (min-width: map-get($grid-breakpoints, 'md')) { + padding: 1.5rem 2rem; + + img { + margin-bottom: 0.5rem; + height: 30px; + } + } + + &:not(.router-link-exact-active) { + background-image: none !important; + background-color: white; + color: $primary; + } + + &.router-link-exact-active:hover { + color: white; + } + + &:first-of-type { + margin-left: 0; + } + &:last-of-type { + margin-right: 0; + } +} diff --git a/libs/common/src/lib/scss/_card.scss b/libs/common/src/lib/scss/_card.scss new file mode 100644 index 000000000..d67942326 --- /dev/null +++ b/libs/common/src/lib/scss/_card.scss @@ -0,0 +1,27 @@ +.card-logo { + display: flex; + border: 0; + flex: 0 0 120px; + background-color: $light; + height: 120px; + width: auto; + display: flex; + justify-content: center; + align-items: center; + border-radius: 10px; + margin-right:1rem; + margin-bottom:1rem; + + img { + margin: auto; + display: block; + } +} + +.card-logo-lg { + max-width: 100%; + max-height: 100%; + width: 180px; + height: 180px; + border-radius: 15px; +} diff --git a/libs/common/src/lib/scss/_contact.scss b/libs/common/src/lib/scss/_contact.scss new file mode 100644 index 000000000..d3d95b715 --- /dev/null +++ b/libs/common/src/lib/scss/_contact.scss @@ -0,0 +1,27 @@ +.section-contact { + background-color: #ffe500; + color: #000; + padding: 5rem 0; + width: 100%; + align-self: center; + + a { + color: $black; + } +} +@media (min-width: 768px) { + .section-contact { + h2 { + font-size: 53px; + + { + p { + font-size: 23px; + } + } + } + padding: 10rem 0; + .align-center { + text-align: center; + } + } +} diff --git a/libs/common/src/lib/scss/_custom-forms.scss b/libs/common/src/lib/scss/_custom-forms.scss new file mode 100644 index 000000000..bf2f1f146 --- /dev/null +++ b/libs/common/src/lib/scss/_custom-forms.scss @@ -0,0 +1,57 @@ +.custom-control { + padding: 0; + margin-bottom: 1rem; +} +.custom-control-label { + display: block; + border: $border-width solid $border-color; + border-radius: $border-radius; + padding: 1rem 0rem; + padding-left: 4rem; + padding-right: 1rem; + + p:last-of-type { + margin: 0; + } +} +.custom-control-label::after, +.custom-control-label::before { + top: 50%; + left: 1.75rem; + margin-top: -0.5rem; +} + +.custom-control-input:checked + .custom-control-label { + border-color: $primary; +} + +.dropdown-select .dropdown-toggle { + border: $border-width solid $border-color; + border-radius: $border-radius; + color: $dark; + display: flex; + width: 100%; + text-align: left; + justify-content: space-between; + align-items: center; + + &:hover, + &:focus, + &:focus:hover { + text-decoration: none; + } + + & > div { + flex: 1; + + img { + vertical-align: text-top; + } + } +} + +.dropdown-select .dropdown-menu { + width: 100%; + max-height: 30vh; + overflow-y: auto; +} diff --git a/libs/common/src/lib/scss/_custom-switch.scss b/libs/common/src/lib/scss/_custom-switch.scss new file mode 100644 index 000000000..9ad1b3472 --- /dev/null +++ b/libs/common/src/lib/scss/_custom-switch.scss @@ -0,0 +1,83 @@ +@mixin switch($res: 'sm') { + $index: 1rem; + $mainVal: 1rem; + + @if $res == 'md' { + $index: 2rem; + $mainVal: 1.5rem; + } @else if $res == 'lg' { + $index: 3rem; + $mainVal: 2rem; + } @else if $res == 'xl' { + $index: 4rem; + $mainVal: 2.5rem; + } + + .custom-control-label { + padding-left: #{$index}; + padding-bottom: #{$mainVal}; + } + + .custom-control-label::before { + height: $mainVal; + width: calc(#{$index} + 0.75rem); + border-radius: $mainVal * 2; + } + + .custom-control-label::after { + width: calc(#{$mainVal} - 4px); + height: calc(#{$mainVal} - 4px); + border-radius: calc(#{$index} - (#{$mainVal} / 2)); + } + + .custom-control-input:checked ~ .custom-control-label::after { + transform: translateX(calc(#{$mainVal} - 0.25rem)); + } +} + +// YOU CAN PUT ALL RESOLUTION HERE +// sm - DEFAULT, md, lg, xl +.custom-switch.custom-switch-sm { + @include switch(); +} + +.custom-switch.custom-switch-md { + @include switch('md'); +} + +.custom-switch.custom-switch-lg { + @include switch('lg'); +} + +.custom-switch.custom-switch-xl { + @include switch('xl'); +} + +.custom-switch .custom-control-input:checked { + ~ .custom-control-label::before { + background-color: $dark !important; + border-color: $dark !important; + } +} + +.custom-switch .custom-control-input:focus { + ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgb(0 0 0 / 25%); + } +} + +.custom-switch-light .custom-control-input:checked { + ~ .custom-control-label::before { + background-color: $light !important; + border-color: $light !important; + } + ~ .custom-control-label::after { + background-color: $darker !important; + } +} + +.custom-switch-light .custom-control-input:focus { + ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgb(255 255 255 / 25%); + } +} diff --git a/libs/common/src/lib/scss/_dark-mode.scss b/libs/common/src/lib/scss/_dark-mode.scss new file mode 100644 index 000000000..23fcf21c5 --- /dev/null +++ b/libs/common/src/lib/scss/_dark-mode.scss @@ -0,0 +1,253 @@ + + +.dark-mode { + background-color: $darkest !important; + + body { + background-color: $darkest !important; + color: $light !important; + } + + // Views + .section-home > .container-md > .row:nth-child(1) > .col-md-4:before { + background-color: $dark !important; + } + + // Sidebar + .sidebar { + background-color: $darker; + + .nav-link-plain .nav-link { + color: $light; + } + + .nav-link-plain { + .router-link-active, + .nav-link:not(.has-children):hover { + background-color: $primary; + + .nav-link-icon i, + span { + color: $light !important; + } + } + } + } + + .sidebar-sibling { + border-color: $gray-900; + } + + // Tabs + .nav-tabs { + border-color: $dark; + .nav-link { + color: $gray; + } + } + .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus, + .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { + background-color: $dark; + color: $white; + border-color: $dark; + } + + // Cards + .card, + .card.bg-white, + .card-body.bg-light, + .list-group-item { + background-color: $darker !important; + color: $gray-500 !important; + border-color: $dark; + + .bg-dark, + .bg-darker { + background-color: $darkest !important; + } + } + + .card-header.bg-light { + background-color: $darkest !important; + color: $gray; + + .badge-dark { + background-color: $dark !important; + } + } + + // Pagination + .page-item:not(.active) .page-link { + background-color: $dark; + border-color: $darker; + color: white + } + .page-item.disabled .page-link { + background-color: $darker; + border-color: $dark; + } + + // Dropdown + .dropdown-select .dropdown-toggle, + .show > .dropdown-toggle, + .show > .dropdown-toggle.btn-light, + .show > .dropdown-toggle.btn-light:active { + background-color: $dark; + border-color: $dark; + color: $gray; + } + .dropdown-menu { + background-color: $darker; + color: $gray; + border-color: $dark; + } + .dropdown-divider { + border-color: $dark; + } + .dropdown-item { + color: $gray; + + &:hover, + &:focus { + background-color: $dark; + } + } + + // Table + .table { + color: $gray-400; + + .custom-checkbox .custom-control-label { + background-color: $darker; + } + } + + .table th, .table td { + border-color: $dark !important; + } + + .table thead tr, + .table thead tr td, + .table-hover thead tr { + background-color: $dark !important; + + .custom-control-label { + background-color: $dark !important; + } + } + .table-hover tbody tr { + background-color: $darker !important; + } + + .table-hover tbody tr:hover { + background-color: $dark !important; + color: $gray-400; + + .custom-control-label { + background-color: $dark !important; + } + } + + tr.bg-light.text-gray { + color: $gray-800 !important; + } + + // Nav + .nav-pills .nav-link.active, .nav-pills .show > .nav-link { + background-color: $primary; + color: $white; + } + + // Badge + .badge-light { + background-color: $dark; + color: $gray; + } + + // Forms + .b-form-btn-label-control.form-control > .form-control, + .b-form-btn-label-control.form-control[aria-disabled=true], .b-form-btn-label-control.form-control[aria-readonly=true] { + border-color: $darker; + background-color: $darkest; + } + .col-form-label { + color: $gray; + } + + .custom-checkbox { + .custom-control-label { + background-color: $darkest; + } + + .custom-control-label::before { + background-color: $darkest; + border-color: $dark; + } + + .custom-control-input:checked ~ .custom-control-label::before { + background-color: $primary !important; + } + } + + .custom-select, + .custom-file-label, + .custom-control-label { + border-color: $darker; + background-color: $darkest; + } + + .custom-file-label, + .custom-file-label::after { + border-color: $darker; + background-color: $darkest; + color: $gray; + } + + .input-group-append button, + .input-group-text { + border-color: $darkest; + background-color: $primary; + color: $white; + } + + .form-control { + border-color: $darkest; + background-color: $darkest; + color: white; + } + + // Buttons + .btn .b-icon.bi, .nav-link .b-icon.bi, .dropdown-toggle .b-icon.bi, .dropdown-item .b-icon.bi, .input-group-text .b-icon.bi { + color: $gray; + } + .btn-light, + .btn-light:focus:active { + border-color: $dark; + background-color: $dark; + color: $gray-500; + } + + // Modals + .modal { + .modal-header { + color: white; + border-color: $dark; + } + .modal-content, + .modal-body { + background-color: $darker !important; + border-color: $dark; + } + .modal-footer { + border-color: $dark; + } + + p { + color: white; + } + + .close { + color: white; + } + } +} \ No newline at end of file diff --git a/libs/common/src/lib/scss/_dropdown.scss b/libs/common/src/lib/scss/_dropdown.scss new file mode 100644 index 000000000..9a7504638 --- /dev/null +++ b/libs/common/src/lib/scss/_dropdown.scss @@ -0,0 +1,24 @@ +.b-dropdown-text { + font-weight: bold; +} + +.dropdown-select { + display: flex; +} + +.dropdown-select .dropdown-item { + white-space: normal; + border-bottom: 1px solid $light; + + &:last-child { + border-bottom: 0; + } +} +.dropdown-item:hover, .dropdown-item:focus { + background-color: $light; + color: $dark; + + a:hover { + color: $primary !important; + } +} \ No newline at end of file diff --git a/libs/common/src/lib/scss/_faq.scss b/libs/common/src/lib/scss/_faq.scss new file mode 100644 index 000000000..e3b04ab5e --- /dev/null +++ b/libs/common/src/lib/scss/_faq.scss @@ -0,0 +1,88 @@ +.section-faq { + h5 { + margin: 0; + border-bottom: 1px solid #efefef; + a { + display: block; + padding: 1rem 0; + color: #000; + font-weight: 700; + font-size: 1rem; + cursor: pointer; + } + + { + div { + padding: 0; + p { + margin: 1rem 0 0; + } + } + } + } +} + +.faq-header { + background-size: cover; + z-index: 0; + background-image: url(./assets/images/faq.jpg); + padding: 10rem 0; + color: #fff; + position: relative; + text-align: center; + + &:after { + content: ''; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + display: block; + position: absolute; + top: 0; + z-index: -1; + } + h2 { + text-align: center; + font-size: 23px; + font-weight: 700; + margin: 0; + color: #ffe500; + + { + p { + font-size: 18px; + font-weight: 400; + } + } + } +} +.faq-content { + border-top: 15px solid #ffe500; + overflow: hidden; + padding: 4rem 0; + position: relative; + &:before { + content: ''; + display: block; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + left: 50%; + width: 40px; + height: 40px; + background-color: #ffe500; + position: absolute; + margin-left: -20px; + top: -20px; + } +} + +@media (min-width: 768px) { + .faq-header { + h2 { + font-size: 53px; + + { + p { + font-size: 23px; + } + } + } + } +} diff --git a/libs/common/src/lib/scss/_forms.scss b/libs/common/src/lib/scss/_forms.scss new file mode 100644 index 000000000..944c2d21d --- /dev/null +++ b/libs/common/src/lib/scss/_forms.scss @@ -0,0 +1,13 @@ +a i.fa-question-circle { + color: $gray-400; +} +select[multiple='multiple'] { + padding: 0 !important; +} +select option { + padding: 0.5rem; +} +select option:checked { + background-color: var(--primary); + color: white; +} diff --git a/libs/common/src/lib/scss/_glossary.scss b/libs/common/src/lib/scss/_glossary.scss new file mode 100644 index 000000000..9aba3503e --- /dev/null +++ b/libs/common/src/lib/scss/_glossary.scss @@ -0,0 +1,100 @@ +.section-glossary { + h2 { + margin: 0; + font-size: 23px; + font-weight: 700; + text-align: center; + + { + p { + font-size: 18px; + font-weight: 400; + } + } + } + background-color: #ffe500; + color: #000; + background: #fff; + padding: 4rem 0; + .btn { + margin-right: 0; + } +} +.glossary-content { + border-top: 15px solid #ffe500; + overflow: hidden; + padding: 4rem 0 0; + position: relative; + &:before { + content: ''; + display: block; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + left: 50%; + width: 40px; + height: 40px; + background-color: #ffe500; + position: absolute; + margin-left: -20px; + top: -20px; + } +} +.glossary-header { + background-size: cover; + z-index: 0; + background-image: url(./assets/images/glossary.jpg); + padding: 10rem 0; + color: #fff; + position: relative; + &:after { + content: ''; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + display: block; + position: absolute; + top: 0; + z-index: 0; + } + .container { + z-index: 1; + } + h2 { + text-align: center; + font-size: 23px; + font-weight: 700; + margin: 0; + color: #ffe500; + + { + p { + font-size: 18px; + font-weight: 400; + } + } + } +} + +@media (min-width: 768px) { + .glossary-header { + h2 { + font-size: 53px; + + { + p { + font-size: 23px; + } + } + } + } +} + +@media (min-width: 768px) { + .section-glossary { + h2 { + font-size: 53px; + + { + p { + font-size: 23px; + } + } + } + } +} diff --git a/libs/common/src/lib/scss/_hooper.scss b/libs/common/src/lib/scss/_hooper.scss new file mode 100644 index 000000000..ced94c0a0 --- /dev/null +++ b/libs/common/src/lib/scss/_hooper.scss @@ -0,0 +1,119 @@ +section .hooper-pagination { + bottom: -4rem; + width: 100%; + + .hooper-indicators { + width: 100%; + + &:before { + content: ''; + position: absolute; + display: block; + width: auth; + left: 1rem; + right: 1rem; + background-color: $gray; + height: 1px; + bottom: 24px; + z-index: -1; + } + + li { + flex: 1; + text-align: center; + } + } + + .hooper-indicator { + background-color: transparent; + width: 100%; + height: 40px; + + &:after { + content: ''; + display: block; + width: 25%; + height: 6px; + border-radius: 6px; + } + + &.is-active:after { + background-color: $darker; + } + } +} +section .hooper-team { + height: auto; +} + +section .hooper-use-cases { + height: auto; +} + +.card-metrics-dark { + margin-top: 100%; + margin-bottom: 1rem; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + margin: 0; + position: absolute; + top: 25%; + right: 0; + margin-right: -200px; + } +} + +section .hooper-metrics { + height: 100%; + + .card { + margin-right: 1rem; + } + + .hooper-track { + height: auto; + } + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + .hooper-list { + position: relative; + overflow: visible; + } + + .hooper-track { + height: 100%; + } + + .hooper-slide { + position: absolute; + height: auto; + + .card { + width: 400px !important; + margin: 0; + } + + &:nth-child(1) { + bottom: 40%; + right: 0; + margin-right: 0px; + } + + &:nth-child(2) { + left: 0; + bottom: 30%; + margin-left: -350px; + + .card { + width: 450px !important; + } + } + + &:nth-child(3) { + left: 0; + bottom: 10%; + margin-left: -150px; + } + } + } +} diff --git a/libs/common/src/lib/scss/_identicon.scss b/libs/common/src/lib/scss/_identicon.scss new file mode 100644 index 000000000..e3bbe9b1a --- /dev/null +++ b/libs/common/src/lib/scss/_identicon.scss @@ -0,0 +1,15 @@ +.identicon { + position: fixed; + z-index: 1; + width: 50px; + height: 50px; + border-radius: 0.25rem; + float: right; + display: flex; + align-items: center; + padding: 0; + justify-content: center; + right: 10px; + top: 10px; + border: 0; +} diff --git a/libs/common/src/lib/scss/_jumbotron.scss b/libs/common/src/lib/scss/_jumbotron.scss new file mode 100644 index 000000000..78a274504 --- /dev/null +++ b/libs/common/src/lib/scss/_jumbotron.scss @@ -0,0 +1,76 @@ +.jumbotron { + display: flex; + flex-direction: column; + padding: 0 !important; + background-size: cover; + + &.large { + min-height: 85vh; + align-items: center; + } + + &:not(.jumbotron-header) .container { + margin: auto; + padding-top: 8rem; + padding-bottom: 5rem; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + padding-top: 5rem; + padding-bottom: 0; + } + } + + .brand-intro { + display: flex; + align-items: center; + } + + .brand-text { + font-size: 3rem !important; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + font-size: 3.5rem !important; + } + } + + .brand-demo { + img { + padding: 1rem; + border-radius: 1rem !important; + width: 100%; + height: auto; + pointer-events: none; + user-select: none; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + margin-top: 100px; + margin-bottom: -130px; + height: 850px; + width: auto; + } + } + } + + .calendly-widget { + width: 100%; + min-width: 320px; + height: 620px; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + height: 700px; + } + } +} + +.jumbotron-header { + min-height: 250px; + max-height: none; + display: flex; + align-items: center; + flex-direction: row; + height: auto; + background-size: cover; + background-position: center bottom; + border-radius: 0; + color: white; +} diff --git a/libs/common/src/lib/scss/_modals.scss b/libs/common/src/lib/scss/_modals.scss new file mode 100644 index 000000000..2a2e94a70 --- /dev/null +++ b/libs/common/src/lib/scss/_modals.scss @@ -0,0 +1,24 @@ +.modal-header { + border: 0; + + h5 { + font-size: 1.5rem; + font-weight: bold; + } + + @media (min-width: map-get($grid-breakpoints, 'md')) { + padding: 3rem 3rem 0 3rem; + } +} +.modal-body { + @media (min-width: map-get($grid-breakpoints, 'md')) { + padding: 2rem 3rem; + } +} + +.modal-footer { + border: 0; + @media (min-width: map-get($grid-breakpoints, 'md')) { + padding: 0rem 3rem 3rem 3rem; + } +} diff --git a/libs/common/src/lib/scss/_navbar.scss b/libs/common/src/lib/scss/_navbar.scss new file mode 100644 index 000000000..7881bc699 --- /dev/null +++ b/libs/common/src/lib/scss/_navbar.scss @@ -0,0 +1,284 @@ +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: var(--primary); + background-color: #e9ecef; +} + +.nav-pills .nav-link { + padding: .5rem 1rem; +} + +.navbar { + z-index: 1; + width: 100%; + + .header-brand { + img { + display: block; + width: 40px; + height: auto; + margin: auto; + + @media (min-width: 992px) { + width: 55px; + } + } + } + + #nav-collapse { + .header-brand { + margin-right: 3rem; + } + + > .navbar-nav { + flex: 1 !important; + } + } + + .navbar-nav-right { + flex: 0 auto; + display: flex; + justify-content: flex-end; + align-items: center; + } + + .navbar-toggler { + border: 0; + transition: 0.2s ease color; + + &:hover { + color: black; + } + } +} + +div:not(.bg-white) > .navbar-text-white.navbar-light .fa-bars { + color: $white; +} + +div:not(.bg-white) > .navbar-text-white.navbar-light .navbar-nav .nav-link { + color: $light; + + &:focus { + color: $secondary; + } + + &:after { + position: absolute; + content: ''; + } + + &:hover { + color: $light; + } + + &.router-link-active { + color: $secondary; + } + + &:hover:after, + &.router-link-active:after { + background-color: $secondary; + } +} + +.navbar.fixed { + position: fixed; + z-index: 1; + background-color: $secondary; + + #nav-collapse > .navbar-nav { + flex: 1 !important; + } +} + +.navbar-light .navbar-nav .nav-link { + display: block; + position: relative; + font-weight: 400; + opacity: 1 !important; + margin: 0 1rem; + padding-right: 0; + padding-left: 0; + padding-bottom: 1rem; + color: $dark; + + &:focus { + box-shadow: none; + color: $primary; + } + + &:after { + position: absolute; + content: ''; + } + + &:hover { + color: $dark; + } + + &.router-link-active { + color: $primary; + } + + &:hover:after, + &.router-link-active:after { + width: 100%; + opacity: 1; + } +} + +.navbar-light .navbar-nav .nav-link:after { + display: block; + height: 2px; + width: 0%; + bottom: 0; + opacity: 0; + color: $primary; + background-color: $primary; + -webkit-transition: 0.2s width, 0.3s opacity; + transition: 0.2s width, 0.3s opacity; +} + +.nav-tabs .nav-link { + padding: 0.5rem 1rem; +} + +.nav-link-plain a::after { + content: unset !important; + display: none !important; +} + +@media (max-width: map-get($grid-breakpoints, 'lg')) { + + #nav-collapse { + margin: .5rem -1rem 0; + background: $white; + color: $dark; + padding: 0; + overflow: auto; + + .nav-link { + color: $black; + font-weight: bold; + padding: 1rem; + padding-left: 2rem; + + &:hover, + &.router-link-active { + background-color: $primary; + height: 100%; + z-index: 0; + border-radius: 25px; + color: $white; + } + + &:hover:after, + &.router-link-active:after { + display: none; + } + } + + > .navbar-nav { + flex: 0 auto; + } + + + .navbar-nav-right { + flex: 0; + border-top: 1px solid $light; + padding: 1rem 2rem; + flex-wrap: wrap; + + a { + flex: 0 100%; + margin: 0.5rem !important; + + &.btn-outline-light { + color: $black; + border-color: $black; + } + } + + .navbar-nav { + flex-direction: row; + flex: 1; + justify-content: space-around; + } + } + + .header-brand { + display: none; + } + } +} + +.navbar.bg-light.navbar-white { + box-shadow: 0 0 50px rgba(0, 0, 0, 0.15); + background-color: white; + padding: 5px 1rem; + + .nav-link { + border-radius: 50%; + height: 60px; + width: 60px; + display: flex; + align-items: center; + justify-content: center; + margin: auto; + + i { + font-size: 1.2rem; + } + + &.router-link-exact-active { + background-color: $gray-200; + } + } + + .nav-link.disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .nav-item:nth-child(2) .nav-link { + position: absolute; + background-color: $secondary; + width: 80px; + height: 80px; + left: 50%; + margin-left: -40px; + margin-top: -10px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); + + i { + font-size: 1.5rem; + } + + &:active { + background: darken($secondary, 5%); + } + + &.router-link-exact-active { + background-color: $secondary; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); + } + } +} + +.navbar-text-white { + .navbar-nav .nav-link { + color: #fff; + font-weight: 400; + &:hover { + color: $secondary; + } + } +} + +.fixed-top.bg-white.shadow .navbar-text-white { + .navbar-nav .nav-link { + color: $dark; + font-weight: 700; + } +} diff --git a/libs/common/src/lib/scss/_number.scss b/libs/common/src/lib/scss/_number.scss new file mode 100644 index 000000000..d31318a88 --- /dev/null +++ b/libs/common/src/lib/scss/_number.scss @@ -0,0 +1,11 @@ +.number { + color: #ffe500; + display: block; + font-size: 3rem; + font-weight: 700; + small { + font-weight: 400; + font-size: 1rem; + display: block; + } +} diff --git a/libs/common/src/lib/scss/_popover.scss b/libs/common/src/lib/scss/_popover.scss new file mode 100644 index 000000000..1c7a075aa --- /dev/null +++ b/libs/common/src/lib/scss/_popover.scss @@ -0,0 +1,6 @@ +.popover { + min-width: 230px; +} +.popover-header { + background-color: $primary; +} diff --git a/libs/common/src/lib/scss/_root.scss b/libs/common/src/lib/scss/_root.scss new file mode 100644 index 000000000..d63658c85 --- /dev/null +++ b/libs/common/src/lib/scss/_root.scss @@ -0,0 +1,246 @@ +html, +body { + min-height: 100%; + font-size: 16px; +} + +#app { + display: flex; + flex-direction: column; + height: 100%; +} + +.pt-lg-10 { + @media (min-width: map-get($grid-breakpoints, 'lg')) { + padding-top: 10rem !important; + } +} + +.pb-lg-10 { + @media (min-width: map-get($grid-breakpoints, 'lg')) { + padding-bottom: 10rem !important; + } +} + +.container-pricing-max-height { + max-height: none; + @media (min-width: map-get($grid-breakpoints, 'md')) { + max-height: 750px; + } + @media (min-width: map-get($grid-breakpoints, 'xl')) { + max-height: 850px; + + } +} + +.table-hover tbody tr { + transition: .2s ease background-color; + + &:hover { + background-color: $light; + } +} + +h1, +.h1 { + font-family: $font-family-sans-serif; + font-weight: 800 !important; + text-transform: uppercase; + line-height: 1; + font-size: 2rem !important; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + font-size: 2.8rem !important; + } +} + +h3, +.h3 { + color: $white; + font-weight: $font-weight-bold; + font-size: 1.5rem; +} + +.h4 { + color: $primary; + text-transform: uppercase; + font-weight: bold; + font-size: 1rem; + border-left: 3px solid $primary; + padding-left: 1rem; + margin-bottom: 1rem; +} + +.h5 { + display: inline-block; + font-size: 1.1rem; + padding: 0.65rem 1rem; + border-radius: 5px; + background-color: $secondary; + + & + .lead { + font-size: 2.2rem; + font-weight: $font-weight-bold; + margin-bottom: 1rem; + line-height: 1.3; + } +} + +.font-size-l { + font-size: 2rem !important; + @media (min-width: map-get($grid-breakpoints, 'lg')) { + font-size: 2rem !important; + } + @media (min-width: map-get($grid-breakpoints, 'xl')) { + font-size: 2.5rem !important; + } +} + +.font-size-xl { + font-size: 4rem !important; +} + +.line-height-2 { + line-height: 2; +} + +.center-center { + display: flex; + align-items: center; + justify-content: center; +} + +.icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 50px; + height: 50px; + background: $dark; + + i { + font-size: 1.2rem; + } + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + width: 80px; + height: 80px; + + i { + font-size: 2rem; + } + } +} + +.bg-before-dark { + position: relative; + + &:before { + content: ''; + background-color: $darker; + position: absolute; + width: 100%; + height: 325px; + top: -1px; + display: block; + } +} + +.bg-before-dark-contact { + position: relative; + + &:before { + background-color: $darker; + position: absolute; + width: 100%; + left: 0; + bottom: 0; + height: 100px; + display: block; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + content: ''; + } + } +} +.btn-icon { + width: 50px !important; + height: 50px !important; + align-items: center; + justify-content: center; + display: inline-flex; + margin: 0 1rem; + + i { + margin: 0 !important; + font-size: 1.3rem !important; + } +} + +.list-disc { + padding: 0; + + li { + margin-bottom: 0.5rem; + + a { + color: $white; + + &:before { + content: ''; + display: inline-block; + width: 3px; + height: 3px; + background-color: $secondary; + margin-right: 0.5rem; + margin-bottom: 0.2rem; + } + } + } +} + +.card-about-us { + .card-header { + height: 460px; + background-size: cover; + } +} + +.card-contact { + .card-header { + height: 200px; + border-top-right-radius: 10px; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + height: 400px; + } + } +} + +.form-control-underlined, +.form-control-underlined:focus { + background: transparent; + box-shadow: none !important; + border: 0; + border-radius: 0; + border-bottom: 1px solid $gray; + color: $white; + padding-left: 0; + padding-right: 0; +} +.form-control-underlined:focus { + border-color: $secondary; +} + +textarea.form-control-underlined { + resize: none; + height: 150px; +} + +.cursor-pointer { + cursor: pointer; + + &:hover { + cursor: pointer; + } +} diff --git a/libs/common/src/lib/scss/_sidebar.scss b/libs/common/src/lib/scss/_sidebar.scss new file mode 100644 index 000000000..b2678adb7 --- /dev/null +++ b/libs/common/src/lib/scss/_sidebar.scss @@ -0,0 +1,127 @@ +html, +body { + height: 100%; +} + +@media (min-width: map-get($grid-breakpoints, 'md')) { + .sidebar-sibling { + margin-left: 260px; + border-left: 1px solid $gray-200; + } +} + +.b-sidebar { + width: 260px; +} + +.b-sidebar-outer ~ .sidebar-sibling { + height: 100%; +} + +.sidebar { + display: flex; + height: 100%; + padding: 0; + background-color: white; + flex-wrap: wrap; + flex-direction: column; + + .navbar-nav, + .nav-item { + width: 100%; + } + + .navbar-nav { + flex-direction: column; + } + + .header-brand img { + width: 65px; + } + + .navbar-nav .nav-link { + padding: 0.75rem 1rem 0.75rem 1rem; + font-weight: 500; + color: $dark; + } + + .navbar-nav .nav-link i { + opacity: 0.75; + } + + .navbar-nav .nav-link.router-link-exact-active, + .navbar-nav .nav-link.router-link-exact-active i { + color: $secondary; + opacity: 1; + } + + .nav-link-plain { + .nav-link { + padding: 0rem; + margin: 0 0.5rem 1px; + border-radius: 5px; + display: block; + transition: 0.2s background-color ease; + + i { + color: $gray-500; + } + + &:not(.has-children):hover { + background-color: $gray-200; + } + } + + .router-link-active { + background-color: $gray-200; + + span { + color: $primary; + } + + i { + color: $primary !important; + } + } + + .nav-link-wrapper { + padding: 0.5rem .25rem; + display: flex; + flex-flow: column; + + &.has-children { + padding: 0rem .25rem; + } + } + + .nav-link-icon { + width: 40px; + flex: 0 0 40px; + display: flex; + align-items: center; + justify-content: center; + + ~ div { + flex-grow: 1; + } + } + + .nav-link-plain { + .nav-link { + padding: 0.7rem; + } + } + } +} + +.navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-direction: column; + width: 100%; +} + +// @media (min-width: map-get($grid-breakpoints, 'md')) { +// .navbar-expand-lg .navbar-collapse { +// display: block !important; +// } +// } diff --git a/libs/common/src/lib/scss/_tab.scss b/libs/common/src/lib/scss/_tab.scss new file mode 100644 index 000000000..644815ccb --- /dev/null +++ b/libs/common/src/lib/scss/_tab.scss @@ -0,0 +1,12 @@ +.tab-content { + > .tab-pane { + display: none; + } + > .active { + display: block; + } + padding: 2rem 0; +} +.blog-header { + padding: 4rem 0; +} diff --git a/libs/common/src/lib/scss/_team.scss b/libs/common/src/lib/scss/_team.scss new file mode 100644 index 000000000..e69de29bb diff --git a/libs/common/src/lib/scss/_token-distribution.scss b/libs/common/src/lib/scss/_token-distribution.scss new file mode 100644 index 000000000..d6803949c --- /dev/null +++ b/libs/common/src/lib/scss/_token-distribution.scss @@ -0,0 +1,25 @@ +.table-token-allocation { + min-width: 1040px; + width: 100%; + + tbody tr { + background-color: white; + transition: ease-in-out background-color 0.2s; + + &:hover { + background-color: $yellow !important; + } + + &:last-of-type { + td:first-child { + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; + } + + td:last-child { + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; + } + } + } +} diff --git a/libs/common/src/lib/scss/_utilities.scss b/libs/common/src/lib/scss/_utilities.scss new file mode 100644 index 000000000..b666537be --- /dev/null +++ b/libs/common/src/lib/scss/_utilities.scss @@ -0,0 +1,39 @@ +.pt-10 { + padding-top: 5rem; +} +.font-size-10 { + font-size: 5rem !important; + + @media (min-width: map-get($grid-breakpoints, 'xl')) { + font-size: 9rem !important; + } +} +.mt-lg-10 { + @media (min-width: map-get($grid-breakpoints, 'xl')) { + margin-top: 7rem !important; + } +} +.ml-lg-10 { + @media (min-width: map-get($grid-breakpoints, 'xl')) { + margin-left: 8rem !important; + } +} +.mt-n7 { + margin-top: -7rem; +} +.bg-gray-1000 { + background-color: $gray-1000; +} +.text-truncate { + width: 150px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; +} +.text-overflow-ellipsis { + overflow: hidden; + text-overflow: ellipsis; +} +.cursor-not-allowed { + cursor: not-allowed; +} diff --git a/libs/common/src/lib/scss/_variables.scss b/libs/common/src/lib/scss/_variables.scss new file mode 100644 index 000000000..7ebca55c6 --- /dev/null +++ b/libs/common/src/lib/scss/_variables.scss @@ -0,0 +1,1125 @@ +// Variables +// +// Variables should follow the `$component-state-property-size` formula for +// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. + +// Color system + +$white: #fff !default; +$gray-100: #f8f9fa !default; +$gray-200: #e9ecef !default; +$gray-300: #dee2e6 !default; +$gray-400: #ced4da !default; +$gray-500: #adb5bd !default; +$gray-600: #6c757d !default; +$gray-700: #495057 !default; +$gray-800: #343a40 !default; +$gray-900: #212529 !default; +$gray-1000: #16131b !default; +$black: #000 !default; + +$grays: () !default; +$grays: map-merge( + ( + '100': $gray-100, + '200': $gray-200, + '300': $gray-300, + '400': $gray-400, + '500': $gray-500, + '600': $gray-600, + '700': $gray-700, + '800': $gray-800, + '900': $gray-900, + '1000': $gray-1000, + ), + $grays +); + +$blue: #007bff !default; +$indigo: #6610f2 !default; +$purple: #5942c1 !default; +$pink: #e83e8c !default; +$red: #dc3545 !default; +$orange: #fd7e14 !default; +$yellow: #ffe500 !default; +$green: #28a745 !default; +$teal: #20c997 !default; +$cyan: #17a2b8 !default; + +$colors: () !default; +$colors: map-merge( + ( + 'blue': $blue, + 'indigo': $indigo, + 'purple': $purple, + 'pink': $pink, + 'red': $red, + 'orange': $orange, + 'yellow': $yellow, + 'green': $green, + 'teal': $teal, + 'cyan': $cyan, + 'white': $white, + 'gray': $gray-600, + 'gray-dark': $gray-800, + ), + $colors +); + +$primary: $purple !default; +$secondary: $yellow !default; +$success: $green !default; +$info: $cyan !default; +$warning: $yellow !default; +$danger: $red !default; +$light: $gray-100 !default; +// $dark: $gray-800 !default; +$gray: #9c9e9f !default; +// $dark: #343a40 !default; +// $darker: #212529 !default; + +$dark: #272934 !default; +$darker: #1f2129 !default; +$darkest: #17181f !default; + +$theme-colors: () !default; +$theme-colors: map-merge( + ( + 'primary': $primary, + 'secondary': $secondary, + 'success': $success, + 'info': $info, + 'warning': $warning, + 'danger': $danger, + 'light': $light, + 'gray': $gray, + 'darker': $darker, + ), + $theme-colors +); + +// Set a specific jump point for requesting color jumps +$theme-color-interval: 8% !default; + +// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255. +$yiq-contrasted-threshold: 150 !default; + +// Customize the light and dark text colors for use in our YIQ color contrast function. +$yiq-text-dark: $gray-900 !default; +$yiq-text-light: $white !default; + +// Characters which are escaped by the escape-svg function +$escaped-characters: (('<', '%3c'), ('>', '%3e'), ('#', '%23'), ('(', '%28'), (')', '%29')) !default; + +// Options +// +// Quickly modify global styling by enabling or disabling optional features. + +$enable-caret: true !default; +$enable-rounded: true !default; +$enable-shadows: false !default; +$enable-gradients: false !default; +$enable-transitions: true !default; +$enable-prefers-reduced-motion-media-query: true !default; +$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS +$enable-grid-classes: true !default; +$enable-pointer-cursor-for-buttons: true !default; +$enable-print-styles: true !default; +$enable-responsive-font-sizes: false !default; +$enable-validation-icons: true !default; +$enable-deprecation-messages: true !default; + +// Spacing +// +// Control the default styling of most Bootstrap elements by modifying these +// variables. Mostly focused on spacing. +// You can add more entries to the $spacers map, should you need more variation. + +$spacer: 1rem !default; +$spacers: () !default; +$spacers: map-merge( + ( + 0: 0, + 1: ( + $spacer * 0.25, + ), + 2: ( + $spacer * 0.5, + ), + 3: $spacer, + 4: ( + $spacer * 1.5, + ), + 5: ( + $spacer * 3, + ), + ), + $spacers +); + +// This variable affects the `.h-*` and `.w-*` classes. +$sizes: () !default; +$sizes: map-merge( + ( + 25: 25%, + 50: 50%, + 75: 75%, + 100: 100%, + auto: auto, + ), + $sizes +); + +// Body +// +// Settings for the `<body>` element. + +$body-bg: $white !default; +$body-color: $gray-900 !default; + +// Links +// +// Style anchor elements. + +$link-color: theme-color('primary') !default; +$link-decoration: none !default; +$link-hover-color: darken($link-color, 15%) !default; +$link-hover-decoration: underline !default; +// Darken percentage for links with `.text-*` class (e.g. `.text-success`) +$emphasized-link-hover-darken-percentage: 15% !default; + +// Paragraphs +// +// Style p element. + +$paragraph-margin-bottom: 1rem !default; + +// Grid breakpoints +// +// Define the minimum dimensions at which your layout will change, +// adapting to different screen sizes, for use in media queries. + +$grid-breakpoints: ( + xs: 0, + sm: 576px, + md: 768px, + lg: 992px, + xl: 1200px, +) !default; + +@include _assert-ascending($grid-breakpoints, '$grid-breakpoints'); +@include _assert-starts-at-zero($grid-breakpoints, '$grid-breakpoints'); + +// Grid containers +// +// Define the maximum width of `.container` for different screen sizes. + +$container-max-widths: ( + sm: 540px, + md: 720px, + lg: 960px, + xl: 1500px, +) !default; + +@include _assert-ascending($container-max-widths, '$container-max-widths'); + +// Grid columns +// +// Set the number of columns and specify the width of the gutters. + +$grid-columns: 12 !default; +$grid-gutter-width: 20px !default; +$grid-row-columns: 6 !default; + +// Components +// +// Define common padding and border radius sizes and more. + +$line-height-lg: 1.5 !default; +$line-height-sm: 1.5 !default; + +$border-width: 1px !default; +$border-color: $gray-300 !default; + +$border-radius: 0.3rem !default; +$border-radius-lg: 0.3rem !default; +$border-radius-sm: 0.2rem !default; + +$rounded-pill: 50rem !default; + +$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075) !default; +$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default; +$box-shadow-lg: 0 1rem 3rem rgba($black, 0.175) !default; + +$component-active-color: $white !default; +$component-active-bg: theme-color('primary') !default; + +$caret-width: 0.3em !default; +$caret-vertical-align: $caret-width * 0.85 !default; +$caret-spacing: $caret-width * 0.85 !default; + +$transition-base: all 0.2s ease-in-out !default; +$transition-fade: opacity 0.15s linear !default; +$transition-collapse: height 0.35s ease !default; + +$embed-responsive-aspect-ratios: () !default; +$embed-responsive-aspect-ratios: join(((21 9), (16 9), (4 3), (1 1)), $embed-responsive-aspect-ratios); + +// Typography +// +// Font, line-height, and color for body text, headings, and more. + +// stylelint-disable value-keyword-case +$font-family-sans-serif: 'Exo 2', BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', + 'Liberation Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' !default; +$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace !default; +$font-family-base: $font-family-sans-serif !default; +// stylelint-enable value-keyword-case + +$font-size-base: 1rem !default; // Assumes the browser default, typically `16px` +$font-size-lg: $font-size-base * 1.25 !default; +$font-size-sm: $font-size-base * 0.875 !default; + +$font-weight-lighter: lighter !default; +$font-weight-light: 300 !default; +$font-weight-normal: 400 !default; +$font-weight-bold: 700 !default; +$font-weight-bolder: bolder !default; + +$font-weight-base: $font-weight-normal !default; +$line-height-base: 1.5 !default; + +$h1-font-size: $font-size-base * 3 !default; +$h2-font-size: $font-size-base * 2 !default; +$h3-font-size: $font-size-base * 1.75 !default; +$h4-font-size: $font-size-base * 1.5 !default; +$h5-font-size: $font-size-base * 1.25 !default; +$h6-font-size: $font-size-base !default; + +$headings-margin-bottom: $spacer / 2 !default; +$headings-font-family: null !default; +$headings-font-weight: 500 !default; +$headings-line-height: 1.2 !default; +$headings-color: null !default; + +$display1-size: 6rem !default; +$display2-size: 5.5rem !default; +$display3-size: 4.5rem !default; +$display4-size: 3.5rem !default; + +$display1-weight: 300 !default; +$display2-weight: 300 !default; +$display3-weight: 300 !default; +$display4-weight: 300 !default; +$display-line-height: $headings-line-height !default; + +$lead-font-size: $font-size-base * 1.25 !default; +$lead-font-weight: $font-weight-normal !default; + +$small-font-size: 80% !default; + +$text-muted: $gray-600 !default; + +$blockquote-small-color: $gray-600 !default; +$blockquote-small-font-size: $small-font-size !default; +$blockquote-font-size: $font-size-base * 1.25 !default; + +$hr-border-color: rgba($black, 0.1) !default; +$hr-border-width: $border-width !default; + +$mark-padding: 0.2em !default; + +$dt-font-weight: $font-weight-bold !default; + +$kbd-box-shadow: inset 0 -0.1rem 0 rgba($black, 0.25) !default; +$nested-kbd-font-weight: $font-weight-bold !default; + +$list-inline-padding: 0.5rem !default; + +$mark-bg: #fcf8e3 !default; + +$hr-margin-y: $spacer !default; + +// Tables +// +// Customizes the `.table` component with basic values, each used across all table variations. + +$table-cell-padding: 0.75rem !default; +$table-cell-padding-sm: 0.3rem !default; + +$table-color: $body-color !default; +$table-bg: null !default; +$table-accent-bg: rgba($black, 0.05) !default; +$table-hover-color: $table-color !default; +$table-hover-bg: rgba($black, 0.075) !default; +$table-active-bg: $table-hover-bg !default; + +$table-border-width: $border-width !default; +$table-border-color: $border-color !default; + +$table-head-bg: $gray-200 !default; +$table-head-color: $gray-700 !default; +$table-th-font-weight: null !default; + +$table-dark-color: $white !default; +$table-dark-bg: $gray-800 !default; +$table-dark-accent-bg: rgba($white, 0.05) !default; +$table-dark-hover-color: $table-dark-color !default; +$table-dark-hover-bg: rgba($white, 0.075) !default; +$table-dark-border-color: lighten($table-dark-bg, 7.5%) !default; + +$table-striped-order: odd !default; + +$table-caption-color: $text-muted !default; + +$table-bg-level: -9 !default; +$table-border-level: -6 !default; + +// Buttons + Forms +// +// Shared variables that are reassigned to `$input-` and `$btn-` specific variables. + +$input-btn-padding-y: 0.75rem !default; +$input-btn-padding-x: 1.2rem !default; +$input-btn-font-family: null !default; +$input-btn-font-size: $font-size-base !default; +$input-btn-line-height: $line-height-base !default; + +$input-btn-focus-width: 0.2rem !default; +$input-btn-focus-color: rgba($component-active-bg, 0.25) !default; +$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default; + +$input-btn-padding-y-sm: 0.25rem !default; +$input-btn-padding-x-sm: 0.5rem !default; +$input-btn-font-size-sm: $font-size-sm !default; +$input-btn-line-height-sm: $line-height-sm !default; + +$input-btn-padding-y-lg: 0.5rem !default; +$input-btn-padding-x-lg: 1rem !default; +$input-btn-font-size-lg: $font-size-lg !default; +$input-btn-line-height-lg: $line-height-lg !default; + +$input-btn-border-width: $border-width !default; + +// Buttons +// +// For each of Bootstrap's buttons, define text, background, and border color. + +$btn-padding-y: $input-btn-padding-y !default; +$btn-padding-x: $input-btn-padding-x !default; +$btn-font-family: $input-btn-font-family !default; +$btn-font-size: $input-btn-font-size !default; +$btn-line-height: $input-btn-line-height !default; +$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping + +$btn-padding-y-sm: $input-btn-padding-y-sm !default; +$btn-padding-x-sm: $input-btn-padding-x-sm !default; +$btn-font-size-sm: $input-btn-font-size-sm !default; +$btn-line-height-sm: $input-btn-line-height-sm !default; + +$btn-padding-y-lg: $input-btn-padding-y-lg !default; +$btn-padding-x-lg: $input-btn-padding-x-lg !default; +$btn-font-size-lg: $input-btn-font-size-lg !default; +$btn-line-height-lg: $input-btn-line-height-lg !default; + +$btn-border-width: $input-btn-border-width !default; + +$btn-font-weight: $font-weight-normal !default; +$btn-box-shadow: inset 0 1px 0 rgba($white, 0.15), 0 1px 1px rgba($black, 0.075) !default; +$btn-focus-width: $input-btn-focus-width !default; +$btn-focus-box-shadow: $input-btn-focus-box-shadow !default; +$btn-disabled-opacity: 0.65 !default; +$btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125) !default; + +$btn-link-disabled-color: $gray-600 !default; + +$btn-block-spacing-y: 0.5rem !default; + +// Allows for customizing button radius independently from global border radius +$btn-border-radius: $border-radius !default; +$btn-border-radius-lg: $border-radius-lg !default; +$btn-border-radius-sm: $border-radius-sm !default; + +$btn-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, + box-shadow 0.15s ease-in-out !default; + +// Forms + +$label-margin-bottom: 0.5rem !default; + +$input-padding-y: $input-btn-padding-y !default; +$input-padding-x: $input-btn-padding-x !default; +$input-font-family: $input-btn-font-family !default; +$input-font-size: $input-btn-font-size !default; +$input-font-weight: $font-weight-base !default; +$input-line-height: $input-btn-line-height !default; + +$input-padding-y-sm: $input-btn-padding-y-sm !default; +$input-padding-x-sm: $input-btn-padding-x-sm !default; +$input-font-size-sm: $input-btn-font-size-sm !default; +$input-line-height-sm: $input-btn-line-height-sm !default; + +$input-padding-y-lg: $input-btn-padding-y-lg !default; +$input-padding-x-lg: $input-btn-padding-x-lg !default; +$input-font-size-lg: $input-btn-font-size-lg !default; +$input-line-height-lg: $input-btn-line-height-lg !default; + +$input-bg: $white !default; +$input-disabled-bg: $gray-200 !default; + +$input-color: $gray-700 !default; +$input-border-color: $gray-400 !default; +$input-border-width: $input-btn-border-width !default; +$input-box-shadow: inset 0 1px 1px rgba($black, 0.075) !default; + +$input-border-radius: $border-radius !default; +$input-border-radius-lg: $border-radius-lg !default; +$input-border-radius-sm: $border-radius-sm !default; + +$input-focus-bg: $input-bg !default; +$input-focus-border-color: lighten($component-active-bg, 25%) !default; +$input-focus-color: $input-color !default; +$input-focus-width: $input-btn-focus-width !default; +$input-focus-box-shadow: $input-btn-focus-box-shadow !default; + +$input-placeholder-color: $gray-600 !default; +$input-plaintext-color: $body-color !default; + +$input-height-border: $input-border-width * 2 !default; + +$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default; +$input-height-inner-half: add($input-line-height * 0.5em, $input-padding-y) !default; +$input-height-inner-quarter: add($input-line-height * 0.25em, $input-padding-y / 2) !default; + +$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default; +$input-height-sm: add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default; +$input-height-lg: add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default; + +$input-transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !default; + +$form-text-margin-top: 0.25rem !default; + +$form-check-input-gutter: 1.25rem !default; +$form-check-input-margin-y: 0.3rem !default; +$form-check-input-margin-x: 0.25rem !default; + +$form-check-inline-margin-x: 0.75rem !default; +$form-check-inline-input-margin-x: 0.3125rem !default; + +$form-grid-gutter-width: 10px !default; +$form-group-margin-bottom: 1rem !default; + +$input-group-addon-color: $input-color !default; +$input-group-addon-bg: $gray-200 !default; +$input-group-addon-border-color: $input-border-color !default; + +$custom-forms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, + box-shadow 0.15s ease-in-out !default; + +$custom-control-gutter: 0.5rem !default; +$custom-control-spacer-x: 1rem !default; +$custom-control-cursor: null !default; + +$custom-control-indicator-size: 1rem !default; +$custom-control-indicator-bg: $input-bg !default; + +$custom-control-indicator-bg-size: 50% 50% !default; +$custom-control-indicator-box-shadow: $input-box-shadow !default; +$custom-control-indicator-border-color: $gray-500 !default; +$custom-control-indicator-border-width: $input-border-width !default; + +$custom-control-label-color: null !default; + +$custom-control-indicator-disabled-bg: $input-disabled-bg !default; +$custom-control-label-disabled-color: $gray-600 !default; + +$custom-control-indicator-checked-color: $component-active-color !default; +$custom-control-indicator-checked-bg: $component-active-bg !default; +$custom-control-indicator-checked-disabled-bg: rgba(theme-color('primary'), 0.5) !default; +$custom-control-indicator-checked-box-shadow: null !default; +$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default; + +$custom-control-indicator-focus-box-shadow: $input-focus-box-shadow !default; +$custom-control-indicator-focus-border-color: $input-focus-border-color !default; + +$custom-control-indicator-active-color: $component-active-color !default; +$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default; +$custom-control-indicator-active-box-shadow: null !default; +$custom-control-indicator-active-border-color: $custom-control-indicator-active-bg !default; + +$custom-checkbox-indicator-border-radius: $border-radius !default; +$custom-checkbox-indicator-icon-checked: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'><path fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/></svg>") !default; + +$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default; +$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default; +$custom-checkbox-indicator-icon-indeterminate: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'><path stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/></svg>") !default; +$custom-checkbox-indicator-indeterminate-box-shadow: null !default; +$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default; + +$custom-radio-indicator-border-radius: 50% !default; +$custom-radio-indicator-icon-checked: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'><circle r='3' fill='#{$custom-control-indicator-checked-color}'/></svg>") !default; + +$custom-switch-width: $custom-control-indicator-size * 1.75 !default; +$custom-switch-indicator-border-radius: $custom-control-indicator-size / 2 !default; +$custom-switch-indicator-size: subtract( + $custom-control-indicator-size, + $custom-control-indicator-border-width * 4 +) !default; + +$custom-select-padding-y: $input-padding-y !default; +$custom-select-padding-x: $input-padding-x !default; +$custom-select-font-family: $input-font-family !default; +$custom-select-font-size: $input-font-size !default; +$custom-select-height: $input-height !default; +$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator +$custom-select-font-weight: $input-font-weight !default; +$custom-select-line-height: $input-line-height !default; +$custom-select-color: $input-color !default; +$custom-select-disabled-color: $gray-600 !default; +$custom-select-bg: $input-bg !default; +$custom-select-disabled-bg: $gray-200 !default; +$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions +$custom-select-indicator-color: $gray-800 !default; +$custom-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'><path fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/></svg>") !default; +$custom-select-background: escape-svg($custom-select-indicator) right $custom-select-padding-x center / + $custom-select-bg-size no-repeat !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon) + +$custom-select-feedback-icon-padding-right: add( + 1em * 0.75, + (2 * $custom-select-padding-y * 0.75) + $custom-select-padding-x + $custom-select-indicator-padding +) !default; +$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default; +$custom-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default; + +$custom-select-border-width: $input-border-width !default; +$custom-select-border-color: $input-border-color !default; +$custom-select-border-radius: $border-radius !default; +$custom-select-box-shadow: inset 0 1px 2px rgba($black, 0.075) !default; + +$custom-select-focus-border-color: $input-focus-border-color !default; +$custom-select-focus-width: $input-focus-width !default; +$custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width $input-btn-focus-color !default; + +$custom-select-padding-y-sm: $input-padding-y-sm !default; +$custom-select-padding-x-sm: $input-padding-x-sm !default; +$custom-select-font-size-sm: $input-font-size-sm !default; +$custom-select-height-sm: $input-height-sm !default; + +$custom-select-padding-y-lg: $input-padding-y-lg !default; +$custom-select-padding-x-lg: $input-padding-x-lg !default; +$custom-select-font-size-lg: $input-font-size-lg !default; +$custom-select-height-lg: $input-height-lg !default; + +$custom-range-track-width: 100% !default; +$custom-range-track-height: 0.5rem !default; +$custom-range-track-cursor: pointer !default; +$custom-range-track-bg: $gray-300 !default; +$custom-range-track-border-radius: 1rem !default; +$custom-range-track-box-shadow: inset 0 0.25rem 0.25rem rgba($black, 0.1) !default; + +$custom-range-thumb-width: 1rem !default; +$custom-range-thumb-height: $custom-range-thumb-width !default; +$custom-range-thumb-bg: $component-active-bg !default; +$custom-range-thumb-border: 0 !default; +$custom-range-thumb-border-radius: 1rem !default; +$custom-range-thumb-box-shadow: 0 0.1rem 0.25rem rgba($black, 0.1) !default; +$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default; +$custom-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge +$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default; +$custom-range-thumb-disabled-bg: $gray-500 !default; + +$custom-file-height: $input-height !default; +$custom-file-height-inner: $input-height-inner !default; +$custom-file-focus-border-color: $input-focus-border-color !default; +$custom-file-focus-box-shadow: $input-focus-box-shadow !default; +$custom-file-disabled-bg: $input-disabled-bg !default; + +$custom-file-padding-y: $input-padding-y !default; +$custom-file-padding-x: $input-padding-x !default; +$custom-file-line-height: $input-line-height !default; +$custom-file-font-family: $input-font-family !default; +$custom-file-font-weight: $input-font-weight !default; +$custom-file-color: $input-color !default; +$custom-file-bg: $input-bg !default; +$custom-file-border-width: $input-border-width !default; +$custom-file-border-color: $input-border-color !default; +$custom-file-border-radius: $input-border-radius !default; +$custom-file-box-shadow: $input-box-shadow !default; +$custom-file-button-color: $custom-file-color !default; +$custom-file-button-bg: $input-group-addon-bg !default; +$custom-file-text: ( + en: 'Browse', +) !default; + +// Form validation + +$form-feedback-margin-top: $form-text-margin-top !default; +$form-feedback-font-size: $small-font-size !default; +$form-feedback-valid-color: theme-color('success') !default; +$form-feedback-invalid-color: theme-color('danger') !default; + +$form-feedback-icon-valid-color: $form-feedback-valid-color !default; +$form-feedback-icon-valid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'><path fill='#{$form-feedback-icon-valid-color}' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/></svg>") !default; +$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default; +$form-feedback-icon-invalid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='#{$form-feedback-icon-invalid-color}' viewBox='0 0 12 12'><circle cx='6' cy='6' r='4.5'/><path stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/><circle cx='6' cy='8.2' r='.6' fill='#{$form-feedback-icon-invalid-color}' stroke='none'/></svg>") !default; + +$form-validation-states: () !default; +$form-validation-states: map-merge( + ( + 'valid': ( + 'color': $form-feedback-valid-color, + 'icon': $form-feedback-icon-valid, + ), + 'invalid': ( + 'color': $form-feedback-invalid-color, + 'icon': $form-feedback-icon-invalid, + ), + ), + $form-validation-states +); + +// Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. + +$zindex-dropdown: 1000 !default; +$zindex-sticky: 1020 !default; +$zindex-fixed: 1030 !default; +$zindex-modal-backdrop: 1040 !default; +$zindex-modal: 1050 !default; +$zindex-popover: 1060 !default; +$zindex-tooltip: 1070 !default; + +// Navs + +$nav-link-padding-y: 1rem !default; +$nav-link-padding-x: 1rem !default; +$nav-link-disabled-color: $gray-600 !default; + +$nav-tabs-border-color: $gray-300 !default; +$nav-tabs-border-width: $border-width !default; +$nav-tabs-border-radius: $border-radius !default; +$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default; +$nav-tabs-link-active-color: $gray-700 !default; +$nav-tabs-link-active-bg: $body-bg !default; +$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default; + +$nav-pills-border-radius: $border-radius !default; +$nav-pills-link-active-color: $component-active-color !default; +$nav-pills-link-active-bg: $component-active-bg !default; + +$nav-divider-color: $gray-200 !default; +$nav-divider-margin-y: $spacer / 2 !default; + +// Navbar + +$navbar-padding-y: $spacer / 2 !default; +$navbar-padding-x: $spacer !default; + +$navbar-nav-link-padding-x: 0.5rem !default; + +$navbar-brand-font-size: $font-size-lg !default; +// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link +$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default; +$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default; +$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default; + +$navbar-toggler-padding-y: 0.25rem !default; +$navbar-toggler-padding-x: 0.75rem !default; +$navbar-toggler-font-size: $font-size-lg !default; +$navbar-toggler-border-radius: $btn-border-radius !default; + +$navbar-nav-scroll-max-height: 75vh !default; + +$navbar-dark-color: rgba($white, 0.5) !default; +$navbar-dark-hover-color: rgba($white, 0.75) !default; +$navbar-dark-active-color: $white !default; +$navbar-dark-disabled-color: rgba($white, 0.25) !default; +$navbar-dark-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'><path stroke='#{$navbar-dark-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default; +$navbar-dark-toggler-border-color: rgba($white, 0.1) !default; + +$navbar-light-color: rgba($black, 0.5) !default; +$navbar-light-hover-color: rgba($black, 0.7) !default; +$navbar-light-active-color: rgba($black, 0.9) !default; +$navbar-light-disabled-color: rgba($black, 0.3) !default; +$navbar-light-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'><path stroke='#{$navbar-light-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default; +$navbar-light-toggler-border-color: rgba($black, 0.1) !default; + +$navbar-light-brand-color: $navbar-light-active-color !default; +$navbar-light-brand-hover-color: $navbar-light-active-color !default; +$navbar-dark-brand-color: $navbar-dark-active-color !default; +$navbar-dark-brand-hover-color: $navbar-dark-active-color !default; + +// Dropdowns +// +// Dropdown menu container and contents. + +$dropdown-min-width: 10rem !default; +$dropdown-padding-x: 0 !default; +$dropdown-padding-y: 0.5rem !default; +$dropdown-spacer: 0.125rem !default; +$dropdown-font-size: $font-size-base !default; +$dropdown-color: $body-color !default; +$dropdown-bg: $white !default; +$dropdown-border-color: rgba($black, 0.15) !default; +$dropdown-border-radius: $border-radius !default; +$dropdown-border-width: $border-width !default; +$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default; +$dropdown-divider-bg: $gray-200 !default; +$dropdown-divider-margin-y: $nav-divider-margin-y !default; +$dropdown-box-shadow: 0 0.5rem 1rem rgba($black, 0.175) !default; + +$dropdown-link-color: $gray-900 !default; +$dropdown-link-hover-color: white !default; +$dropdown-link-hover-bg: $primary !default; + +$dropdown-link-active-color: $component-active-color !default; +$dropdown-link-active-bg: $component-active-bg !default; + +$dropdown-link-disabled-color: $gray-500 !default; + +$dropdown-item-padding-y: 0.5rem !default; +$dropdown-item-padding-x: 1.5rem !default; + +$dropdown-header-color: $gray-600 !default; +$dropdown-header-padding: $dropdown-padding-y $dropdown-item-padding-x !default; + +// Pagination + +$pagination-padding-y: 0.5rem !default; +$pagination-padding-x: 0.75rem !default; +$pagination-padding-y-sm: 0.25rem !default; +$pagination-padding-x-sm: 0.5rem !default; +$pagination-padding-y-lg: 0.75rem !default; +$pagination-padding-x-lg: 1.5rem !default; +$pagination-line-height: 1.25 !default; + +$pagination-color: $link-color !default; +$pagination-bg: $white !default; +$pagination-border-width: $border-width !default; +$pagination-border-color: $gray-300 !default; + +$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default; +$pagination-focus-outline: 0 !default; + +$pagination-hover-color: $link-hover-color !default; +$pagination-hover-bg: $gray-200 !default; +$pagination-hover-border-color: $gray-300 !default; + +$pagination-active-color: $component-active-color !default; +$pagination-active-bg: $component-active-bg !default; +$pagination-active-border-color: $pagination-active-bg !default; + +$pagination-disabled-color: $gray-600 !default; +$pagination-disabled-bg: $white !default; +$pagination-disabled-border-color: $gray-300 !default; + +$pagination-border-radius-sm: $border-radius-sm !default; +$pagination-border-radius-lg: $border-radius-lg !default; + +// Jumbotron + +$jumbotron-padding: 2rem !default; +$jumbotron-color: null !default; +$jumbotron-bg: $primary !default; + +// Cards + +$card-spacer-y: 0.75rem !default; +$card-spacer-x: 1.25rem !default; +$card-border-width: $border-width !default; +$card-border-radius: $border-radius !default; +$card-border-color: rgba($black, 0.125) !default; +$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default; +$card-cap-bg: rgba($black, 0.03) !default; +$card-cap-color: null !default; +$card-height: null !default; +$card-color: null !default; +$card-bg: $white !default; + +$card-img-overlay-padding: 1.25rem !default; + +$card-group-margin: $grid-gutter-width / 2 !default; +$card-deck-margin: $card-group-margin !default; + +$card-columns-count: 3 !default; +$card-columns-gap: 1.25rem !default; +$card-columns-margin: $card-spacer-y !default; + +// Tooltips + +$tooltip-font-size: $font-size-sm !default; +$tooltip-max-width: 200px !default; +$tooltip-color: $white !default; +$tooltip-bg: $black !default; +$tooltip-border-radius: $border-radius !default; +$tooltip-opacity: 0.9 !default; +$tooltip-padding-y: 0.25rem !default; +$tooltip-padding-x: 0.5rem !default; +$tooltip-margin: 0 !default; + +$tooltip-arrow-width: 0.8rem !default; +$tooltip-arrow-height: 0.4rem !default; +$tooltip-arrow-color: $tooltip-bg !default; + +// Form tooltips must come after regular tooltips +$form-feedback-tooltip-padding-y: $tooltip-padding-y !default; +$form-feedback-tooltip-padding-x: $tooltip-padding-x !default; +$form-feedback-tooltip-font-size: $tooltip-font-size !default; +$form-feedback-tooltip-line-height: $line-height-base !default; +$form-feedback-tooltip-opacity: $tooltip-opacity !default; +$form-feedback-tooltip-border-radius: $tooltip-border-radius !default; + +// Popovers + +$popover-font-size: $font-size-sm !default; +$popover-bg: $white !default; +$popover-max-width: 276px !default; +$popover-border-width: $border-width !default; +$popover-border-color: rgba($black, 0.2) !default; +$popover-border-radius: $border-radius-lg !default; +$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default; +$popover-box-shadow: 0 0.25rem 0.5rem rgba($black, 0.2) !default; + +$popover-header-bg: darken($popover-bg, 3%) !default; +$popover-header-color: $headings-color !default; +$popover-header-padding-y: 0.5rem !default; +$popover-header-padding-x: 0.75rem !default; + +$popover-body-color: $body-color !default; +$popover-body-padding-y: $popover-header-padding-y !default; +$popover-body-padding-x: $popover-header-padding-x !default; + +$popover-arrow-width: 1rem !default; +$popover-arrow-height: 0.5rem !default; +$popover-arrow-color: $popover-bg !default; + +$popover-arrow-outer-color: fade-in($popover-border-color, 0.05) !default; + +// Toasts + +$toast-max-width: 350px !default; +$toast-padding-x: 0.75rem !default; +$toast-padding-y: 0.25rem !default; +$toast-font-size: 0.875rem !default; +$toast-color: null !default; +$toast-background-color: rgba($white, 0.85) !default; +$toast-border-width: 1px !default; +$toast-border-color: rgba(0, 0, 0, 0.1) !default; +$toast-border-radius: 0.25rem !default; +$toast-box-shadow: 0 0.25rem 0.75rem rgba($black, 0.1) !default; + +$toast-header-color: $gray-600 !default; +$toast-header-background-color: rgba($white, 0.85) !default; +$toast-header-border-color: rgba(0, 0, 0, 0.05) !default; + +// Badges + +$badge-font-size: 75% !default; +$badge-font-weight: $font-weight-bold !default; +$badge-padding-y: 0.25em !default; +$badge-padding-x: 0.4em !default; +$badge-border-radius: $border-radius !default; + +$badge-transition: $btn-transition !default; +$badge-focus-width: $input-btn-focus-width !default; + +$badge-pill-padding-x: 0.6em !default; +// Use a higher than normal value to ensure completely rounded edges when +// customizing padding or font-size on labels. +$badge-pill-border-radius: 10rem !default; + +// Modals + +// Padding applied to the modal body +$modal-inner-padding: 1rem !default; + +// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding +$modal-footer-margin-between: 0.5rem !default; + +$modal-dialog-margin: 0.5rem !default; +$modal-dialog-margin-y-sm-up: 1.75rem !default; + +$modal-title-line-height: $line-height-base !default; + +$modal-content-color: null !default; +$modal-content-bg: $white !default; +$modal-content-border-color: rgba($black, 0.2) !default; +$modal-content-border-width: $border-width !default; +$modal-content-border-radius: $border-radius-lg !default; +$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default; +$modal-content-box-shadow-xs: 0 0.25rem 0.5rem rgba($black, 0.5) !default; +$modal-content-box-shadow-sm-up: 0 0.5rem 1rem rgba($black, 0.5) !default; + +$modal-backdrop-bg: $black !default; +$modal-backdrop-opacity: 0.5 !default; +$modal-header-border-color: $border-color !default; +$modal-footer-border-color: $modal-header-border-color !default; +$modal-header-border-width: $modal-content-border-width !default; +$modal-footer-border-width: $modal-header-border-width !default; +$modal-header-padding-y: 1rem !default; +$modal-header-padding-x: 1rem !default; +$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility + +$modal-xl: 1140px !default; +$modal-lg: 800px !default; +$modal-md: 500px !default; +$modal-sm: 300px !default; + +$modal-fade-transform: translate(0, -50px) !default; +$modal-show-transform: none !default; +$modal-transition: transform 0.3s ease-out !default; +$modal-scale-transform: scale(1.02) !default; + +// Alerts +// +// Define alert colors, border radius, and padding. + +$alert-padding-y: 0.75rem !default; +$alert-padding-x: 1.25rem !default; +$alert-margin-bottom: 1rem !default; +$alert-border-radius: $border-radius !default; +$alert-link-font-weight: $font-weight-bold !default; +$alert-border-width: $border-width !default; + +$alert-bg-level: -10 !default; +$alert-border-level: -9 !default; +$alert-color-level: 6 !default; + +// Progress bars + +$progress-height: 1rem !default; +$progress-font-size: $font-size-base * 0.75 !default; +$progress-bg: $gray-200 !default; +$progress-border-radius: $border-radius !default; +$progress-box-shadow: inset 0 0.1rem 0.1rem rgba($black, 0.1) !default; +$progress-bar-color: $white !default; +$progress-bar-bg: theme-color('primary') !default; +$progress-bar-animation-timing: 1s linear infinite !default; +$progress-bar-transition: width 0.6s ease !default; + +// List group + +$list-group-color: null !default; +$list-group-bg: $white !default; +$list-group-border-color: rgba($black, 0.125) !default; +$list-group-border-width: $border-width !default; +$list-group-border-radius: $border-radius !default; + +$list-group-item-padding-y: 0.75rem !default; +$list-group-item-padding-x: 1.25rem !default; + +$list-group-hover-bg: $gray-100 !default; +$list-group-active-color: $component-active-color !default; +$list-group-active-bg: $component-active-bg !default; +$list-group-active-border-color: $list-group-active-bg !default; + +$list-group-disabled-color: $gray-600 !default; +$list-group-disabled-bg: $list-group-bg !default; + +$list-group-action-color: $gray-700 !default; +$list-group-action-hover-color: $list-group-action-color !default; + +$list-group-action-active-color: $body-color !default; +$list-group-action-active-bg: $gray-200 !default; + +// Image thumbnails + +$thumbnail-padding: 0.25rem !default; +$thumbnail-bg: $body-bg !default; +$thumbnail-border-width: $border-width !default; +$thumbnail-border-color: $gray-300 !default; +$thumbnail-border-radius: $border-radius !default; +$thumbnail-box-shadow: 0 1px 2px rgba($black, 0.075) !default; + +// Figures + +$figure-caption-font-size: 90% !default; +$figure-caption-color: $gray-600 !default; + +// Breadcrumbs + +$breadcrumb-font-size: null !default; + +$breadcrumb-padding-y: 0.75rem !default; +$breadcrumb-padding-x: 1rem !default; +$breadcrumb-item-padding: 0.5rem !default; + +$breadcrumb-margin-bottom: 1rem !default; + +$breadcrumb-bg: $gray-200 !default; +$breadcrumb-divider-color: $gray-600 !default; +$breadcrumb-active-color: $gray-600 !default; +$breadcrumb-divider: quote('/') !default; + +$breadcrumb-border-radius: $border-radius !default; + +// Carousel + +$carousel-control-color: $white !default; +$carousel-control-width: 15% !default; +$carousel-control-opacity: 0.5 !default; +$carousel-control-hover-opacity: 0.9 !default; +$carousel-control-transition: opacity 0.15s ease !default; + +$carousel-indicator-width: 30px !default; +$carousel-indicator-height: 3px !default; +$carousel-indicator-hit-area-height: 10px !default; +$carousel-indicator-spacer: 3px !default; +$carousel-indicator-active-bg: $white !default; +$carousel-indicator-transition: opacity 0.6s ease !default; + +$carousel-caption-width: 70% !default; +$carousel-caption-color: $white !default; + +$carousel-control-icon-width: 20px !default; + +$carousel-control-prev-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' width='8' height='8' viewBox='0 0 8 8'><path d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/></svg>") !default; +$carousel-control-next-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' width='8' height='8' viewBox='0 0 8 8'><path d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/></svg>") !default; + +$carousel-transition-duration: 0.6s !default; +$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`) + +// Spinners + +$spinner-width: 2rem !default; +$spinner-height: $spinner-width !default; +$spinner-border-width: 0.25em !default; + +$spinner-width-sm: 1rem !default; +$spinner-height-sm: $spinner-width-sm !default; +$spinner-border-width-sm: 0.2em !default; + +// Close + +$close-font-size: $font-size-base * 1.5 !default; +$close-font-weight: $font-weight-bold !default; +$close-color: $black !default; +$close-text-shadow: 0 1px 0 $white !default; + +// Code + +$code-font-size: 87.5% !default; +$code-color: $pink !default; + +$kbd-padding-y: 0.2rem !default; +$kbd-padding-x: 0.4rem !default; +$kbd-font-size: $code-font-size !default; +$kbd-color: $white !default; +$kbd-bg: $gray-900 !default; + +$pre-color: $gray-900 !default; +$pre-scrollable-max-height: 340px !default; + +// Utilities + +$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default; +$overflows: auto, hidden !default; +$positions: static, relative, absolute, fixed, sticky !default; +$user-selects: all, auto, none !default; + +// Printing + +$print-page-size: a3 !default; +$print-body-min-width: map-get($grid-breakpoints, 'lg') !default; diff --git a/libs/common/src/lib/twitter.ts b/libs/common/src/lib/twitter.ts new file mode 100644 index 000000000..57685481d --- /dev/null +++ b/libs/common/src/lib/twitter.ts @@ -0,0 +1,124 @@ +export class TwitterQuery { + static parse(operators: { [key: string]: string }) { + const ops = Object.keys(operators).reduce((obj: any, key: string) => { + try { + obj[key] = JSON.parse(operators[key]); + } catch (error) { + obj[key] = operators[key]; + } + return obj; + }, {}) as TTwitterOperators; + return ops; + } + + static stringifyField(field: string[] | string | number, isURL = false) { + if (!field || (Array.isArray(field) && !field.length)) return; + if (!Array.isArray(field)) return; + const items = field.filter((value: string) => { + return isURL ? value.length && value !== 'https://' : value.length; + }); + if (!items.length) return; + return JSON.stringify(items); + } + + static stringifyFieldURL(field: string[] | string | number) { + if (!field || (Array.isArray(field) && !field.length)) return; + if (!Array.isArray(field)) return; + const items = field.filter((value: string) => value.length && value !== 'https://'); + if (!items.length) return; + + return JSON.stringify(items.map((url: string) => `"https://${url}"`)); + } + + static stringify(operators: TTwitterOperators) { + return { + from: this.stringifyField(operators['from']), + to: this.stringifyField(operators['to']), + text: this.stringifyField(operators['text']), + url: this.stringifyFieldURL(operators['url']), + hashtags: this.stringifyField(operators['hashtags']), + mentions: this.stringifyField(operators['mentions']), + media: operators['media'], + excludes: this.stringifyField(operators['excludes']), + } as { [key: string]: string }; + } + + static create(operators: TTwitterOperators) { + const operatorKeys = Object.keys(operators); + if (!operatorKeys) return; + const media = + !operators['media'] || ['ignore', null].includes(operators['media']) ? '' : ` ${operators['media']}`; + const query = operatorKeys + .map((key: string) => { + switch (key) { + case 'from': { + const items = operators[key] as string[]; + if (!items) return; + const authors = items + .map((author: string) => `from:${author}`) + .filter((v) => !!v) + .join(' OR '); + return items.length > 1 ? `(${authors})` : authors; + } + case 'to': { + const items = operators[key] as string[]; + if (!items) return; + const authors = items + .map((author: string) => `to:${author}`) + .filter((v) => !!v) + .join(' OR '); + return items.length > 1 ? `(${authors})` : authors; + } + case 'text': { + const items = operators[key] as string[]; + if (!items) return; + const texts = items + .map((value: string) => `"${value}"`) + .filter((v) => !!v) + .join(' OR '); + return items.length > 1 ? `(${texts})` : texts; + } + case 'url': { + const items = operators[key] as string[]; + if (!items) return; + const urls = items + .map((value: string) => `url:${value}`) + .filter((v) => !!v) + .join(' OR '); + return items.length > 1 ? `(${urls})` : urls; + } + case 'hashtags': { + const items = operators[key] as string[]; + if (!items) return; + const hashtags = items + .map((tag: string) => `#${tag}`) + .filter((v) => !!v) + .join(' OR '); + return (items.length > 1 ? `(${hashtags})` : hashtags) + media; + } + case 'mentions': { + const items = operators[key] as string[]; + if (!items) return; + const mentions = items + .map((tag: string) => `@${tag}`) + .filter((v) => !!v) + .join(' OR '); + return (items.length > 1 ? `(${mentions})` : mentions) + media; + } + case 'excludes': { + const items = operators[key] as string[]; + if (!items) return; + return items + .map((type: string) => type) + .filter((v) => !!v) + .join(' '); + } + } + return; + }) + .filter((query) => !!query) + .join(' '); + + return `${query}`; + } +} diff --git a/libs/common/src/lib/types/Account.d.ts b/libs/common/src/lib/types/Account.d.ts new file mode 100644 index 000000000..dfd12c29f --- /dev/null +++ b/libs/common/src/lib/types/Account.d.ts @@ -0,0 +1,25 @@ +type TAccount = { + _id: string; + sub: string; + username: string; + firstName: string; + lastName: string; + profileImg: string; + plan: AccountPlanType; + website: string; + organisation: string; + active: boolean; + isEmailVerified: boolean; + email: string; + address: string; + variant: AccountVariant; + otpSecret: string; + acceptTermsPrivacy: boolean; + acceptUpdates: boolean; + role: Role; + goal: Goal[]; + tokens: TToken[]; + identity: string; + createdAt: Date; + updatedAt: Date; +}; diff --git a/libs/common/src/lib/types/Brand.d.ts b/libs/common/src/lib/types/Brand.d.ts new file mode 100644 index 000000000..2a6d7e4d8 --- /dev/null +++ b/libs/common/src/lib/types/Brand.d.ts @@ -0,0 +1,7 @@ +type TBrand = { + previewImgUrl: string; + logoImgUrl: string; + backgroundImgUrl: string; + widgetPreviewImgUrl: string; + poolId: string; +}; diff --git a/libs/common/src/lib/types/Claim.d.ts b/libs/common/src/lib/types/Claim.d.ts new file mode 100644 index 000000000..a19dfebc1 --- /dev/null +++ b/libs/common/src/lib/types/Claim.d.ts @@ -0,0 +1,11 @@ +type TQRCodeEntry = { + sub: string; + uuid: string; + redirectURL: string; + rewardId: string; + amount: number; + error: string; + account: TAccount; + claimedAt: Date; + createdAt: Date; +}; diff --git a/libs/common/src/lib/types/Client.d.ts b/libs/common/src/lib/types/Client.d.ts new file mode 100644 index 000000000..a0aa2ddde --- /dev/null +++ b/libs/common/src/lib/types/Client.d.ts @@ -0,0 +1,17 @@ +type TClient = { + _id: string; + page: number; + name: string; + sub: string; + poolId: string; + grantType: GrantVariant; + clientId: string; + clientSecret: string; + redirectUri: string; + requestUri: string; + createdAt?: Date; +}; + +type TClientState = { + [poolId: string]: { [clientId: string]: TClient }; +}; diff --git a/libs/common/src/lib/types/Collaborator.d.ts b/libs/common/src/lib/types/Collaborator.d.ts new file mode 100644 index 000000000..836458ef1 --- /dev/null +++ b/libs/common/src/lib/types/Collaborator.d.ts @@ -0,0 +1,9 @@ +type TCollaborator = { + account: TAccount; + state: CollaboratorInviteState; + email: string; + uuid: string; + sub: string; + poolId: string; + updatedAt: Date; +}; diff --git a/libs/common/src/lib/types/DiscordBot.d.ts b/libs/common/src/lib/types/DiscordBot.d.ts new file mode 100644 index 000000000..289519de3 --- /dev/null +++ b/libs/common/src/lib/types/DiscordBot.d.ts @@ -0,0 +1,81 @@ +type TDiscordGuild = { + _id: string; + id: string; + sub: string; + poolId: string; + guildId: string; + channelId: string; + adminRoleId: string; + name: string; + roles: TDiscordRole[]; + channels: TDiscordChannel[]; + isInvited: boolean; + isConnected: boolean; + secret: string; + isShownSecret: boolean; + icon: string; + permissions: any; +}; + +type TDiscordChannel = { + channelId: string; + name: string; +}; + +type TDiscordReaction = { + guildId: string; + messageId: string; + memberId: string; + content: string; +}; + +type TDiscordMessage = { + guildId: string; + messageId: string; + memberId: string; +}; + +type TDiscordRole = { + id: string; + name: string; + color: string; + host: boolean; +}; + +type TDiscordButton = { + label: string; + style: ButtonStyle; + emoji?: string; + customId?: string; + url?: string; + disabled?: boolean; +}; + +type TDiscordUser = { + userId: string; + guildId: string; + profileImgUrl: string; + username: string; + publicMetrics: { + joinedAt: Date; + reactionCount: number; + messageCount: number; + }; +}; + +type TDiscordEmbed = { + title: string; + description: string; + author: { + name: string; + icon_url: string; + url: string; + }; + image: { url: string }; + color: number; + fields: { + name: string; + value: string; + inline?: boolean; + }[]; +}; diff --git a/libs/common/src/lib/types/DiscordRoleReward.d.ts b/libs/common/src/lib/types/DiscordRoleReward.d.ts new file mode 100644 index 000000000..3d01e956e --- /dev/null +++ b/libs/common/src/lib/types/DiscordRoleReward.d.ts @@ -0,0 +1,8 @@ +type TRewardDiscordRole = TReward & { + discordRoleId: string; +}; + +type TRewardDiscordRolePayment = TRewardPayment & { + discordRoleId: string; + role: { color: string; name: string }; +}; diff --git a/libs/common/src/lib/types/ERC1155.d.ts b/libs/common/src/lib/types/ERC1155.d.ts new file mode 100644 index 000000000..00cab507c --- /dev/null +++ b/libs/common/src/lib/types/ERC1155.d.ts @@ -0,0 +1,52 @@ +type TERC1155MetadataProp = { + name: string; + propType: string; + description: string; +}; + +type TERC1155Token = { + _id: string; + sub: string; + state: ERC1155TokenState; + recipient: string; + failReason: string; + transactions: string[]; + tokenId: number; + tokenUri: string; + metadataId: string; + erc1155Id: string; + walletId: string; + metadata: TERC1155Metadata; + balance: string; +}; + +type TERC1155 = { + id?: string; + variant: NFTVariant; + sub: string; + chainId: ChainId; + name: string; + logoImgUrl: string; + transactions?: string[]; + baseURL?: string; + description?: string; + contract?: Contract; + address?: string; + createdAt?: Date; + updatedAt?: Date; + archived?: boolean; +}; + +type TERC1155Metadata = { + _id?: string; + erc1155Id: string; + tokenId: number; + imageUrl: string; + name: string; + image: string; + description: string; + externalUrl: string; + tokens?: TERC1155Token[]; + createdAt: Date; + updatedAt: Date; +}; diff --git a/libs/common/src/lib/types/ERC1155Perk.d.ts b/libs/common/src/lib/types/ERC1155Perk.d.ts new file mode 100644 index 000000000..4dcba5d2a --- /dev/null +++ b/libs/common/src/lib/types/ERC1155Perk.d.ts @@ -0,0 +1,6 @@ +import { TBaseReward } from './BaseReward'; + +export type TERC1155Perk = TBaseReward & { + erc1155Id: string; + erc1155metadataId: string; +}; diff --git a/libs/common/src/lib/types/ERC1155PerkPayment.d.ts b/libs/common/src/lib/types/ERC1155PerkPayment.d.ts new file mode 100644 index 000000000..d80d8a99e --- /dev/null +++ b/libs/common/src/lib/types/ERC1155PerkPayment.d.ts @@ -0,0 +1,6 @@ +export type TERC1155PerkPayment = { + rewardId: string; + sub: string; + poolId: string; + amount: number; +}; diff --git a/libs/common/src/lib/types/ERC20.d.ts b/libs/common/src/lib/types/ERC20.d.ts new file mode 100644 index 000000000..8e83c5d17 --- /dev/null +++ b/libs/common/src/lib/types/ERC20.d.ts @@ -0,0 +1,35 @@ +type TERC20 = { + _id: string; + type: ERC20Type; + name: string; + symbol: string; + address: string; + transactions: string[]; + chainId?: ChainId; + contract?: Contract; + contractName?: TContractName; + sub?: string; + totalSupply: number; + decimals?: number; + adminBalance?: number; + poolBalance?: number; // TODO Should move to TAssetPool + archived: boolean; + logoImgUrl?: string; +}; + +type TERC20Token = { + sub?: string; + erc20Id: string; + balance?: number; + walletId: string; +}; + +type TERC20Transfer = { + erc20Id: string; + erc20: string; + from: string; + to: string; + chainId: ChainId; + transactionId: string; + sub: string; +}; diff --git a/libs/common/src/lib/types/ERC721.d.ts b/libs/common/src/lib/types/ERC721.d.ts new file mode 100644 index 000000000..4e88fee62 --- /dev/null +++ b/libs/common/src/lib/types/ERC721.d.ts @@ -0,0 +1,56 @@ +type TERC721Token = { + _id: string; + sub: string; + state: ERC721TokenState; + recipient: string; + failReason: string; + transactions: string[]; + tokenId: number; + tokenUri: string; + metadataId: string; + erc721Id: string; + metadata: TERC721Metadata; + walletId: string; + balance?: string; +}; + +type TERC721 = { + _id?: string; + variant: NFTVariant; + sub: string; + chainId: ChainId; + name: string; + symbol: string; + transactions?: string[]; + baseURL?: string; + description?: string; + contract?: Contract; + address?: string; + createdAt?: Date; + updatedAt?: Date; + archived?: boolean; + logoImgUrl?: string; +}; + +type TERC721Metadata = { + _id: string; + erc721Id: string; + imageUrl: string; + name: string; + image: string; + description: string; + externalUrl: string; + tokens?: TERC721Token[]; + createdAt?: Date; + updatedAt?: Date; +}; + +type TERC721Transfer = { + erc721Id: string; + erc721TokenId: string; + from: string; + to: string; + chainId: ChainId; + transactionId: string; + sub: string; +}; diff --git a/libs/common/src/lib/types/Event.d.ts b/libs/common/src/lib/types/Event.d.ts new file mode 100644 index 000000000..7abfc4acf --- /dev/null +++ b/libs/common/src/lib/types/Event.d.ts @@ -0,0 +1,8 @@ +type TEvent = { + _id: string; + identity: TIdentity; + identityId: string; + poolId: string; + name: string; + createdAt: Date; +}; diff --git a/libs/common/src/lib/types/Galachain.d.ts b/libs/common/src/lib/types/Galachain.d.ts new file mode 100644 index 000000000..e870fc82a --- /dev/null +++ b/libs/common/src/lib/types/Galachain.d.ts @@ -0,0 +1,13 @@ +type TGalachainContract = { + channelName: string; + chaincodeName: string; + contractName: string; + methodName?: string; +}; + +type TGalachainToken = { + collection: string; + category: string; + type: string; + additionalKey: string; +}; diff --git a/libs/common/src/lib/types/Identity.d.ts b/libs/common/src/lib/types/Identity.d.ts new file mode 100644 index 000000000..d070dadd0 --- /dev/null +++ b/libs/common/src/lib/types/Identity.d.ts @@ -0,0 +1,8 @@ +type TIdentity = { + _id: string; + poolId: string; + uuid: string; + sub: string; + createdAt: Date; + account?: TAccount; +}; diff --git a/libs/common/src/lib/types/Interaction.d.ts b/libs/common/src/lib/types/Interaction.d.ts new file mode 100644 index 000000000..3feedd187 --- /dev/null +++ b/libs/common/src/lib/types/Interaction.d.ts @@ -0,0 +1,22 @@ +type TInteraction = { + iat: number; + exp: number; + session?: + | { + accountId: string; + uid: string; + cookie: string; + acr?: string | undefined; + amr?: string[] | undefined; + } + | undefined; + params: any; + prompt: object; + result?: object | undefined; + returnTo: string; + deviceCode?: string | undefined; + trusted?: string[] | undefined; + uid: string; + lastSubmission?: object | undefined; + grantId?: string | undefined; +}; diff --git a/libs/common/src/lib/types/Invoice.d.ts b/libs/common/src/lib/types/Invoice.d.ts new file mode 100644 index 000000000..242ebbafe --- /dev/null +++ b/libs/common/src/lib/types/Invoice.d.ts @@ -0,0 +1,13 @@ +type TInvoice = { + poolId: string; + additionalUnitCount: number; + costPerUnit: number; + costSubscription: number; + costTotal: number; + currency: string; + mapCount: number; + mapLimit: number; + plan: AccountPlanType; + periodStartDate: Date; + periodEndDate: Date; +}; diff --git a/libs/common/src/lib/types/Job.d.ts b/libs/common/src/lib/types/Job.d.ts new file mode 100644 index 000000000..55eccdc45 --- /dev/null +++ b/libs/common/src/lib/types/Job.d.ts @@ -0,0 +1 @@ +type TJob = Job; diff --git a/libs/common/src/lib/types/Notification.d.ts b/libs/common/src/lib/types/Notification.d.ts new file mode 100644 index 000000000..330c6dfca --- /dev/null +++ b/libs/common/src/lib/types/Notification.d.ts @@ -0,0 +1,8 @@ +type TNotification = { + _id: string; + sub: string; + subjectId: string; + subject: string; + poolId: string; + message: string; +}; diff --git a/libs/common/src/lib/types/Pagination.d.ts b/libs/common/src/lib/types/Pagination.d.ts new file mode 100644 index 000000000..bc2f14065 --- /dev/null +++ b/libs/common/src/lib/types/Pagination.d.ts @@ -0,0 +1,12 @@ +type TPaginationParams = Partial<{ + page: number; + limit: number; + total: number; + previous?: { page: number }; + next?: { page: number }; +}>; + +type TPaginationResult = { + page: number; + total: number; +}; diff --git a/libs/common/src/lib/types/Participant.d.ts b/libs/common/src/lib/types/Participant.d.ts new file mode 100644 index 000000000..a49afc812 --- /dev/null +++ b/libs/common/src/lib/types/Participant.d.ts @@ -0,0 +1,12 @@ +type TParticipant = { + _id: string; + sub: string; + poolId: string; + rank: number; + score: number; + riskAnalysis: { score: number; reasons: string[] }; + questEntryCount: number; + isSubscribed: boolean; + balance: number; + updatedAt: Date; +}; diff --git a/libs/common/src/lib/types/Payment.d.ts b/libs/common/src/lib/types/Payment.d.ts new file mode 100644 index 000000000..c407a6fcb --- /dev/null +++ b/libs/common/src/lib/types/Payment.d.ts @@ -0,0 +1,20 @@ +type TPayment = { + _id: string; + poolId: string; + sub: string; + createdAt: Date; +}; + +type JoinPoolAttributes = { + to: string; + data: string; + minBPTOut: string; + attributes: { joinPoolRequest: JoinPoolRequest }; +}; + +type JoinPoolRequest = { + assets: string[]; + maxAmountsIn: string[]; + userData: string; + fromInternalBalance: boolean; +}; diff --git a/libs/common/src/lib/types/Pool.d.ts b/libs/common/src/lib/types/Pool.d.ts new file mode 100644 index 000000000..4f46a0725 --- /dev/null +++ b/libs/common/src/lib/types/Pool.d.ts @@ -0,0 +1,78 @@ +type TCampaign = { + _id: string; + title: string; + expiryDate: Date; + address: string; + chainId: ChainId; + domain: string; + participants: number; + active: boolean; + progress: number; + tags: string[]; + logoImgUrl?: string; + backgroundImgUrl?: string; + quests: { title: string; description: string; amount: number }[]; + rewards: { title: string; description: string; amount: number }[]; +}; + +type TPool = { + _id: string; + safeAddress: string; + guilds: TDiscordGuild[]; + rank: number; + token: string; + signingSecret: string; + contract: Contract; + chainId: ChainId; + sub: string; + transactions: string[]; + version?: string; + variant?: 'defaultDiamond' | 'registry' | 'factory' | 'sharedWallet'; + events: string[]; + brand: TBrand; + // wallets: TWallet[]; + settings: TPoolSettings; + widget: { domain: string; active: boolean }; + collaborators: TCollaborator[]; + identities: TIdentity[]; + owner: TAccount; + safe: WalletDocument; + campaignURL: string; + createdAt: Date; + trialEndsAt: Date; + balance: string; +}; + +type TPoolSettings = { + title: string; + slug: string; + description: string; + startDate: Date; + endDate?: Date; + isArchived: boolean; + isPublished: boolean; + isWeeklyDigestEnabled: boolean; + isTwitterSyncEnabled: boolean; + discordWebhookUrl: string; + galachainPrivateKey: string; + defaults: { + discordMessage: string; + conditionalRewards: TQuestSocial & { hashtag: string }; + }; + authenticationMethods: AccountVariant[]; +}; + +type TPoolTransfer = { + sub: string; + poolId: string; + token: string; + expiry: Date; +}; + +type TPoolTransferResponse = TPoolTransfer & { + isExpired: boolean; + isTransferred: boolean; + isCopied: boolean; + url: string; + now: number; +}; diff --git a/libs/common/src/lib/types/Quest.d.ts b/libs/common/src/lib/types/Quest.d.ts new file mode 100644 index 000000000..47b379df0 --- /dev/null +++ b/libs/common/src/lib/types/Quest.d.ts @@ -0,0 +1,35 @@ +type TQuest = TQuestDaily | TQuestInvite | TQuestSocial | TQuestCustom | TQuestWeb3 | TQuestGitcoin | TQuestWebhook; +type TQuestEntry = + | TQuestDailyClaim + | TQuestInviteEntry + | TQuestSocialEntry + | TQuestCustomEntry + | TQuestWeb3Entry + | TQuestGitcoinEntry + | TQuestWebhookEntry; + +type TQuestEntryMetadata = TQuestSocialEntryMetadata | TQuestWeb3EntryMetadata; + +type TValidationResult = { + reason: string; + result: boolean; +}; + +type TBaseQuest = { + _id: string; + variant: QuestVariant; + uuid: string; + poolId: string; + title: string; + description: string; + index: number; + image: string; + infoLinks: TInfoLink[]; + expiryDate: Date; + locks: TQuestLock[]; + isPublished: boolean; + createdAt: string; + updatedAt: string; + update: (payload: Partial<TQuest>) => Promise<void>; + delete: (payload: Partial<TQuest>) => Promise<void>; +}; diff --git a/libs/common/src/lib/types/QuestCustom.d.ts b/libs/common/src/lib/types/QuestCustom.d.ts new file mode 100644 index 000000000..6f6b09501 --- /dev/null +++ b/libs/common/src/lib/types/QuestCustom.d.ts @@ -0,0 +1,5 @@ +type TQuestCustom = TBaseQuest & { + amount: number; + limit: number; + eventName: string; +}; diff --git a/libs/common/src/lib/types/QuestCustomEntry.d.ts b/libs/common/src/lib/types/QuestCustomEntry.d.ts new file mode 100644 index 000000000..a07c73a40 --- /dev/null +++ b/libs/common/src/lib/types/QuestCustomEntry.d.ts @@ -0,0 +1,10 @@ +type TQuestCustomEntry = { + questId: string; + sub: string; + uuid: string; + amount: number; + isClaimed: boolean; + poolId: string; + createdAt: Date; + eventName: string; +}; diff --git a/libs/common/src/lib/types/QuestDaily.d.ts b/libs/common/src/lib/types/QuestDaily.d.ts new file mode 100644 index 000000000..9fa696a00 --- /dev/null +++ b/libs/common/src/lib/types/QuestDaily.d.ts @@ -0,0 +1,7 @@ +type TQuestDaily = TBaseQuest & { + amounts: number[]; + progress?: number; + claims?: any[]; + events?: any[]; + eventName: string; +}; diff --git a/libs/common/src/lib/types/QuestDailyEntry.d.ts b/libs/common/src/lib/types/QuestDailyEntry.d.ts new file mode 100644 index 000000000..df13c8246 --- /dev/null +++ b/libs/common/src/lib/types/QuestDailyEntry.d.ts @@ -0,0 +1,14 @@ +type TQuestDailyEntry = { + questId: string; + poolId: string; + sub: string; + uuid: string; + amount: string; + createdAt: Date; + metadata: TQuestDailyEntryMetadata; +}; + +type TQuestDailyEntryMetadata = { + ip: string; + state: DailyRewardClaimState; +}; diff --git a/libs/common/src/lib/types/QuestGitcoin.d.ts b/libs/common/src/lib/types/QuestGitcoin.d.ts new file mode 100644 index 000000000..2342e69d8 --- /dev/null +++ b/libs/common/src/lib/types/QuestGitcoin.d.ts @@ -0,0 +1,19 @@ +type TQuestGitcoin = TBaseQuest & { + amount: number; + scorerId: number; + score: number; +}; + +type TQuestGitcoinEntry = { + poolId: string; + questId: string; + sub: string; + amount: number; + createdAt: Date; + metadata: TQuestGitcoinEntryMetadata; +}; + +type TQuestGitcoinEntryMetadata = { + address: string; + score: number; +}; diff --git a/libs/common/src/lib/types/QuestInvite.d.ts b/libs/common/src/lib/types/QuestInvite.d.ts new file mode 100644 index 000000000..d050e99a1 --- /dev/null +++ b/libs/common/src/lib/types/QuestInvite.d.ts @@ -0,0 +1,7 @@ +type TQuestInvite = TBaseQuest & { + successUrl: string; + pathname: string; + token: string; + amount: number; + isMandatoryReview: boolean; +}; diff --git a/libs/common/src/lib/types/QuestInviteEntry.d.ts b/libs/common/src/lib/types/QuestInviteEntry.d.ts new file mode 100644 index 000000000..794a5f25e --- /dev/null +++ b/libs/common/src/lib/types/QuestInviteEntry.d.ts @@ -0,0 +1,10 @@ +type TQuestInviteEntry = { + questId: string; + sub: string; + uuid: string; + amount: string; + isApproved: boolean; + createdAt: Date; + poolId: string; + metadata: string; +}; diff --git a/libs/common/src/lib/types/QuestSocial.d.ts b/libs/common/src/lib/types/QuestSocial.d.ts new file mode 100644 index 000000000..2122e7d79 --- /dev/null +++ b/libs/common/src/lib/types/QuestSocial.d.ts @@ -0,0 +1,9 @@ +type TQuestSocial = TBaseQuest & { + amount: number; + platform: number; // Deprecate in favor of kind + kind: AccessTokenKind; + interaction: QuestSocialRequirement; + content: string; + contentMetadata: any; + entries?: TQuestSocialEntry[]; +}; diff --git a/libs/common/src/lib/types/QuestSocialEntry.d.ts b/libs/common/src/lib/types/QuestSocialEntry.d.ts new file mode 100644 index 000000000..cd5d15ca8 --- /dev/null +++ b/libs/common/src/lib/types/QuestSocialEntry.d.ts @@ -0,0 +1,17 @@ +type TQuestSocialEntry = { + _id: string; + questId: string; + sub: string; + amount: string; + poolId: string; + metadata: TQuestSocialEntryMetadata; + createdAt: Date; + account?: TAccount; + wallet?: TWallet; +}; + +type TQuestSocialEntryMetadata = { + platformUserId: string; + twitter: TTwitterUserPublicMetrics; + discord: { guildId: string; reactionCount: number; messageCount: number }; +}; diff --git a/libs/common/src/lib/types/QuestWebhook.d.ts b/libs/common/src/lib/types/QuestWebhook.d.ts new file mode 100644 index 000000000..7073ad83f --- /dev/null +++ b/libs/common/src/lib/types/QuestWebhook.d.ts @@ -0,0 +1,5 @@ +type TQuestWebhook = TBaseQuest & { + amount: number; + webhookId: string; + metadata: string; +}; diff --git a/libs/common/src/lib/types/QuestWebhookEntry.d.ts b/libs/common/src/lib/types/QuestWebhookEntry.d.ts new file mode 100644 index 000000000..dab18b0d5 --- /dev/null +++ b/libs/common/src/lib/types/QuestWebhookEntry.d.ts @@ -0,0 +1,9 @@ +type TQuestWebhookEntry = { + poolId: string; + questId: string; + webhookId: string; + identityId: string; + sub: string; + amount: number; + createdAt: Date; +}; diff --git a/libs/common/src/lib/types/Reward.d.ts b/libs/common/src/lib/types/Reward.d.ts new file mode 100644 index 000000000..ed52e2dc3 --- /dev/null +++ b/libs/common/src/lib/types/Reward.d.ts @@ -0,0 +1,45 @@ +type TInfoLink = { + label: string; + url: string; +}; + +type TQuestLock = { variant: QuestVariant; questId: string }; +type TReward = TRewardCoin | TRewardNFT | TRewardCustom | TRewardCoupon | TRewardDiscordRole | TRewardGalachain; +type TBaseReward = { + _id: string; + variant: RewardVariant; + uuid: string; + poolId: string; + title: string; + description: string; + expiryDate: Date; + claimAmount: number; + claimLimit: number; + limit: number; + pointPrice: number; + image: string; + index: number; + locks: TQuestLock[]; + isPromoted: boolean; + isPublished: boolean; + createdAt: string; + updatedAt: string; + update: (payload: Partial<TReward>) => Promise<void>; + delete: (payload: Partial<TReward>) => Promise<void>; +}; + +type TBaseRewardPayment = { + _id: string; + rewardId: string; + poolId: string; + amount: number; + sub: string; + createdAt: Date; +}; +type TRewardPayment = + | TRewardCoinPayment + | TRewardNFTPayment + | TRewardCustomPayment + | TRewardCouponPayment + | TRewardDiscordRolePayment + | TRewardGalachainPayment; diff --git a/libs/common/src/lib/types/RewardCoin.d.ts b/libs/common/src/lib/types/RewardCoin.d.ts new file mode 100644 index 000000000..73aa73b7c --- /dev/null +++ b/libs/common/src/lib/types/RewardCoin.d.ts @@ -0,0 +1,4 @@ +type TRewardCoin = TReward & { + erc20Id: string; + amount: string; +}; diff --git a/libs/common/src/lib/types/RewardCoinPayment.d.ts b/libs/common/src/lib/types/RewardCoinPayment.d.ts new file mode 100644 index 000000000..22798e949 --- /dev/null +++ b/libs/common/src/lib/types/RewardCoinPayment.d.ts @@ -0,0 +1,3 @@ +type TRewardCoinPayment = TBaseRewardPayment & { + // +}; diff --git a/libs/common/src/lib/types/RewardCoupon.d.ts b/libs/common/src/lib/types/RewardCoupon.d.ts new file mode 100644 index 000000000..cfe55f11f --- /dev/null +++ b/libs/common/src/lib/types/RewardCoupon.d.ts @@ -0,0 +1,19 @@ +type TRewardCoupon = TReward & { + codes: string[]; + couponCodes: TCouponCode[]; + webshopURL: string; +}; + +type TRewardCouponPayment = TBaseRewardPayment & { + couponCodeId: string; + code: string; +}; + +type TCouponCode = { + _id: string; + couponRewardId: string; + poolId: string; + code: string; + sub: string; + createdAt: Date; +}; diff --git a/libs/common/src/lib/types/RewardCustom.d.ts b/libs/common/src/lib/types/RewardCustom.d.ts new file mode 100644 index 000000000..29704ac1e --- /dev/null +++ b/libs/common/src/lib/types/RewardCustom.d.ts @@ -0,0 +1,8 @@ +type TRewardCustom = TReward & { + webhookId: string; + metadata: string; +}; + +type TRewardCustomPayment = TBaseRewardPayment & { + // +}; diff --git a/libs/common/src/lib/types/RewardGalachain.d.ts b/libs/common/src/lib/types/RewardGalachain.d.ts new file mode 100644 index 000000000..b5eb381a5 --- /dev/null +++ b/libs/common/src/lib/types/RewardGalachain.d.ts @@ -0,0 +1,12 @@ +type TRewardGalachain = TReward & { + contractChannelName: string; + contractChaincodeName: string; + contractContractName: string; + tokenCollection: string; + tokenCategory: string; + tokenType: string; + tokenAdditionalKey: string; + tokenInstance: number; + amount: string; + privateKey: string; +}; diff --git a/libs/common/src/lib/types/RewardGalachainPayment.d.ts b/libs/common/src/lib/types/RewardGalachainPayment.d.ts new file mode 100644 index 000000000..dfe338574 --- /dev/null +++ b/libs/common/src/lib/types/RewardGalachainPayment.d.ts @@ -0,0 +1,3 @@ +type TRewardGalachainPayment = TBaseRewardPayment & { + amount: string; +}; diff --git a/libs/common/src/lib/types/RewardNFT.d.ts b/libs/common/src/lib/types/RewardNFT.d.ts new file mode 100644 index 000000000..d1d2b3ca6 --- /dev/null +++ b/libs/common/src/lib/types/RewardNFT.d.ts @@ -0,0 +1,13 @@ +type TRewardNFT = TReward & { + erc1155Id: string; + erc721Id: string; + erc1155Amount: number; + metadataId: string; + tokenId: string; + pointPrice: number; + image: string; + isPromoted: boolean; + price: number; + priceCurrency: string; + redirectUrl: string; +}; diff --git a/libs/common/src/lib/types/RewardNFTPayment.d.ts b/libs/common/src/lib/types/RewardNFTPayment.d.ts new file mode 100644 index 000000000..ea3cd452b --- /dev/null +++ b/libs/common/src/lib/types/RewardNFTPayment.d.ts @@ -0,0 +1,3 @@ +type TRewardNFTPayment = TBaseRewardPayment & { + // +}; diff --git a/libs/common/src/lib/types/Token.d.ts b/libs/common/src/lib/types/Token.d.ts new file mode 100644 index 000000000..4f706d2fb --- /dev/null +++ b/libs/common/src/lib/types/Token.d.ts @@ -0,0 +1,12 @@ +type TToken = { + sub: string; + kind: AccessTokenKind; + accessToken: string; + refreshToken: string; + accessTokenEncrypted: string; + refreshTokenEncrypted: string; + scopes: OAuthScope[]; + expiry: number; + userId: string; + metadata: any; +}; diff --git a/libs/common/src/lib/types/Transaction.d.ts b/libs/common/src/lib/types/Transaction.d.ts new file mode 100644 index 000000000..9813e8b3f --- /dev/null +++ b/libs/common/src/lib/types/Transaction.d.ts @@ -0,0 +1,245 @@ +type TTransaction = { + type: TransactionType; + state: TransactionState; + from: string; + to: string; + nonce: number; + gas: string; + chainId: ChainId; + walletId: string; + transactionId: string; + transactionHash?: string; + safeTxHash?: string; + call?: { fn: string; args: string }; + baseFee?: string; + maxFeePerGas?: string; + maxPriorityFeePerGas?: string; + failReason?: string; + callback: TTransactionCallback; +}; + +type TERC20DeployCallbackArgs = { + erc20Id: string; +}; + +type ERC20DeployCallback = { + type: 'Erc20DeployCallback'; + args: TERC20DeployCallbackArgs; +}; + +type TERC721DeployCallbackArgs = { + erc721Id: string; +}; + +type TERC1155DeployCallbackArgs = { + erc1155Id: string; +}; + +type WalletDeployCallback = { + type: 'walletDeployCallback'; + args: TWalletDeployCallbackArgs; +}; + +type TWalletDeployCallbackArgs = { + walletId: string; + owner: string; + sub: string; +}; + +type ERC721DeployCallback = { + type: 'Erc721DeployCallback'; + args: TERC721DeployCallbackArgs; +}; + +type ERC1155DeployCallback = { + type: 'ERC1155DeployCallback'; + args: TERC1155DeployCallbackArgs; +}; + +type TAssetPoolDeployCallbackArgs = { + assetPoolId: string; + chainId: number; +}; + +type PoolDeployCallback = { + type: 'assetPoolDeployCallback'; + args: TAssetPoolDeployCallbackArgs; +}; + +type TTopupCallbackArgs = { + receiver: string; + depositId: string; +}; + +type TopupCallback = { + type: 'topupCallback'; + args: TTopupCallbackArgs; +}; + +type TDepositCallbackArgs = { + assetPoolId: string; + depositId: string; +}; + +type DepositCallback = { + type: 'depositCallback'; + args: TDepositCallbackArgs; +}; + +type TSwapCreateCallbackArgs = { + assetPoolId: string; + swapId: string; +}; + +type SwapCreateCallback = { + type: 'swapCreateCallback'; + args: TSwapCreateCallbackArgs; +}; + +type TERC721TokenMintCallbackArgs = { + erc721tokenId: string; +}; + +type TERC1155TokenMintCallbackArgs = { + erc1155tokenId: string; +}; + +type TERC1155TokenTransferCallbackArgs = { + erc1155tokenId: string; + assetPoolId: string; +}; + +type ERC721TokenMintCallback = { + type: 'erc721TokenMintCallback'; + args: TERC721TokenMintCallbackArgs; +}; + +type ERC1155TokenMintCallback = { + type: 'erc1155TokenMintCallback'; + args: TERC1155TokenMintCallbackArgs; +}; + +type ERC1155TokenTransferCallback = { + type: 'erc1155TokenTransferCallback'; + args: TERC1155TokenTransferCallbackArgs; +}; + +type TPayCallbackArgs = { + paymentId: string; + contractName: TokenContractName; + address: string; +}; + +type PaymentCallback = { + type: 'paymentCallback'; + args: TPayCallbackArgs; +}; + +type TWithdrawForCallbackArgs = { + withdrawalId: string; + assetPoolId: string; +}; + +type WithdrawForCallback = { + type: 'withdrawForCallback'; + args: TWithdrawForCallbackArgs; +}; + +type TWalletGrantRoleCallBackArgs = { + walletId: string; +}; + +type WalletGrantRoleCallBack = { + type: 'grantRoleCallBack'; + args: TWalletGrantRoleCallBackArgs; +}; + +type TWalletRevokeRoleCallBackArgs = { + walletManagerId: string; + walletId: string; +}; + +type WalletRevokeRoleCallBack = { + type: 'revokeRoleCallBack'; + args: TWalletRevokeRoleCallBackArgs; +}; + +type TERC20TransferFromCallBackArgs = { + erc20Id: string; +}; + +type ERC20TransferFromCallBack = { + type: 'transferFromCallBack'; + args: TERC20TransferFromCallBackArgs; +}; + +type ERC721TransferFromCallBack = { + type: 'erc721nTransferFromCallback'; + args: TERC721TransferFromCallBackArgs; +}; + +type TERC721TransferFromCallBackArgs = { + erc721Id: string; + erc721TokenId: string; + walletId: string; +}; + +// This is used for non pool transfers +type ERC721TransferFromWalletCallback = { + type: 'erc721TransferFromWalletCallback'; + args: TERC721TransferFromWalletCallbackArgs; +}; + +type TERC721TransferFromWalletCallbackArgs = { + erc721Id: string; + erc721TokenId: string; + walletId: string; + to: string; +}; + +// This is used for pool transfers +type ERC1155TransferFromCallback = { + type: 'erc1155TransferFromCallback'; + args: TERC1155TransferFromCallbackArgs; +}; + +type TERC1155TransferFromCallbackArgs = { + erc1155Id: string; + erc1155TokenId: string; + walletId: string; +}; + +// This is used for non pool transfers +type ERC1155TransferFromWalletCallback = { + type: 'erc1155TransferFromWalletCallback'; + args: TERC1155TransferFromWalletCallbackArgs; +}; + +type TERC1155TransferFromWalletCallbackArgs = { + erc1155Id: string; + erc1155TokenId: string; + walletId: string; + to: string; +}; + +type TTransactionCallback = + | ERC20DeployCallback + | ERC721DeployCallback + | ERC1155DeployCallback + | PoolDeployCallback + | TopupCallback + | DepositCallback + | SwapCreateCallback + | ERC721TokenMintCallback + | ERC1155TokenMintCallback + | ERC1155TokenTransferCallback + | PaymentCallback + | WithdrawForCallback + | WalletDeployCallback + | WalletGrantRoleCallBack + | WalletRevokeRoleCallBack + | ERC20TransferFromCallBack + | ERC721TransferFromCallBack + | ERC721TransferFromWalletCallback + | ERC1155TransferFromCallback + | ERC1155TransferFromWalletCallback; diff --git a/libs/common/src/lib/types/Twitter.d.ts b/libs/common/src/lib/types/Twitter.d.ts new file mode 100644 index 000000000..a481da822 --- /dev/null +++ b/libs/common/src/lib/types/Twitter.d.ts @@ -0,0 +1,136 @@ +type TTwitterQuery = { + _id: string; + createdAt: Date; + poolId: string; + query: string; + posts: TTwitterPost[]; + operators: TTwitterOperators; + defaults: { + title: string; + description: string; + amount: number; + isPublished: boolean; + minFollowersCount: number; + expiryInDays: number; + locks: TQuestLock[]; + }; +}; + +type TTwitterRequestParams = { + max_results: number; + pagination_token?: string; + since_id?: string; + max_id?: string; + query?: string; +}; + +type TTwitterLike = { + _id: string; + userId: string; + postId: string; +}; + +type TTwitterPostPublicMetrics = { + retweetCount: number; + replyCount: number; + likeCount: number; + quoteCount: number; + bookmarkCount: number; + impressionCount: number; +}; + +type TTwitterPostPublicMetricsResponse = { + retweet_count: number; + reply_count: number; + like_count: number; + quote_count: number; + bookmark_count: number; + impression_count: number; +}; + +type TTwitterPost = { + _id: string; + userId: string; + postId: string; + queryId: string; + text: string; + publicMetrics: TTwitterPostPublicMetrics; +}; + +type TTwitterRepost = { + _id: string; + userId: string; + postId: string; +}; + +type TTwitterFollower = { + _id: string; + userId: string; + targetUserId: string; +}; + +type TTwitterUserPublicMetrics = { + followersCount: number; + followingCount: number; + tweetCount: number; + listedCount: number; + likeCount: number; +}; + +type TTwitterUser = { + _id: string; + userId: string; + profileImgUrl: string; + name: string; + username: string; + publicMetrics: TTwitterUserPublicMetrics; +}; + +type TTwitterUserResponse = { + id: string; + profile_image_url: string; + name: string; + username: string; + public_metrics: { + followers_count: number; + following_count: number; + tweet_count: number; + listed_count: number; + like_count: number; + }; +}; + +type TTwitterOperators = { + from: string[]; + to: string[]; + text: string[]; + url: string[]; + hashtags: string[]; + mentions: string[]; + media: string | null; + excludes: string[]; +}; + +type TTwitterMediaResponse = { + preview_image_url: string; + width: number; + height: number; + type: 'video' | 'photo' | 'animated_gif'; + url: string; +}; + +type TTwitterPostResponse = { + id: string; + author_id: string; + text: string; + created_at: number; + attachments: { + media_keys: string[]; + }; + public_metrics: TTwitterPostPublicMetricsResponse; +}; + +type TTwitterPostWithUserAndMedia = TTwitterPostResponse & { + user: TTwitterUserResponse; + media: TTwitterMediaResponse[]; +}; diff --git a/libs/common/src/lib/types/Wallet.d.ts b/libs/common/src/lib/types/Wallet.d.ts new file mode 100644 index 000000000..fdbe714b1 --- /dev/null +++ b/libs/common/src/lib/types/Wallet.d.ts @@ -0,0 +1,13 @@ +type TWallet = { + _id?: string; + poolId: string; + sub: string; + chainId: ChainId; + address: string; + version: string; + variant: WalletVariant; + safeVersion: string; + uuid: string; + createdAt: Date; + expiresAt: Date; +}; diff --git a/libs/common/src/lib/types/Web3Quest.d.ts b/libs/common/src/lib/types/Web3Quest.d.ts new file mode 100644 index 000000000..431d9c331 --- /dev/null +++ b/libs/common/src/lib/types/Web3Quest.d.ts @@ -0,0 +1,22 @@ +type TQuestWeb3 = TBaseQuest & { + amount: number; + methodName: string; + threshold: string; + contracts: { chainId: ChainId; address: string }[]; +}; + +type TQuestWeb3Entry = { + questId: string; + sub: string; + amount: number; + poolId: string; + createdAt: Date; + metadata: TQuestWeb3EntryMetadata; +}; + +type TQuestWeb3EntryMetadata = { + callResult: string; + chainId: ChainId; + address: string; + rpc?: string; +}; diff --git a/libs/common/src/lib/types/Webhook.d.ts b/libs/common/src/lib/types/Webhook.d.ts new file mode 100644 index 000000000..d236b7916 --- /dev/null +++ b/libs/common/src/lib/types/Webhook.d.ts @@ -0,0 +1,22 @@ +type TWebhook = { + _id?: string; + sub: string; + url: string; + poolId: string; + status: WebhookStatus; + active: boolean; + webhookRequests: TWebhookRequest[]; + createdAt?: Date; +}; + +type TWebhookRequest = { + _id?: string; + webhookId: string; + payload: string; + payloadFormatted?: HighlightResult; + attempts: number; + httpStatus: number; + state: WebhookRequestState; + failReason: string; + createdAt?: Date; +}; diff --git a/libs/common/src/lib/types/Widget.d.ts b/libs/common/src/lib/types/Widget.d.ts new file mode 100644 index 000000000..49c1c5851 --- /dev/null +++ b/libs/common/src/lib/types/Widget.d.ts @@ -0,0 +1,14 @@ +type TWidget = { + uuid: string; + poolId: string; + iconImg: string; + align: string; + message: string; + domain: string; + color: string; + bgColor: string; + cssSelector: string; + theme: string; + active: boolean; + isPublished: boolean; +}; diff --git a/libs/common/tsconfig.json b/libs/common/tsconfig.json index 3cb66fdc3..fd5988eae 100644 --- a/libs/common/tsconfig.json +++ b/libs/common/tsconfig.json @@ -6,6 +6,8 @@ "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, + "noImplicitAny": false, + "resolveJsonModule": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "types": ["node"], diff --git a/libs/sdk/.eslintrc.json b/libs/sdk/.eslintrc.json new file mode 100644 index 000000000..5ace9ff53 --- /dev/null +++ b/libs/sdk/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": "error" + } + } + ] +} diff --git a/libs/sdk/README.md b/libs/sdk/README.md new file mode 100644 index 000000000..6dd775465 --- /dev/null +++ b/libs/sdk/README.md @@ -0,0 +1,250 @@ +# THX Network JS SDK + +This SDK contains API wrappers and an OIDC OAuth manager to simplify access to THX API resources. + +## Prerequisites + +1. [Sign up for an account](https://dashboard.thx.network/) +2. Create API keys (Developer -> API) +3. Save your `clientId` and `clientSecret` + +## SDK Contents + +- [1. THXWidget](#1-thxwidget) + + - [1.1 ContainerSelector](#11-containerselector) + - [1.2 Identity](#12-identity) + +- [2. THXAPIClient](#2-thxapiclient) + + - [2.1 Identities](#21-identities) + - [2.2 Events](#22-events) + +- [3. THXBrowserClient](#3-thxbrowserclient) + + - [3.1 Account](#31-account) + - [3.2 Quests](#32-quests) + - [3.3 Rewards](#33-rewards) + - [3.4 Wallet](#34-wallet) + +## 1. THXWidget + +Meant for loading the HTML widget in a website using JavaScript. + +```javascript +import { THXWidget, THXWidgetOptions } from '@thxnetwork/sdk'; + +const options: THXWidgetOptions = { + campaignId: '6571c9c6b7d775decb45a8f0', + containerSelector: '#your-html-container', // Optional + identity: '36d33a59-5398-463a-ac98-0f7d9b201648', // Optional +}; +THXWidget.create(options); +``` + +### 1.1 ContainerSelector + +Providing a `containerSelector` is optional and will inject the application in a given HTML element. Make sure to provide CSS styles for proper dimensions within your page. + +```html +<div id="your-html-container" style="height: 750px;"></div> +``` + +No messagbox, launcher and notification elements will be injected if a container selector is specified! + +### 1.2 Identity + +Providing an identity is optional and alternatively you can set an identity at a later moment, for example after successful authentication with your app. + +```javascript +window.THXWidget.setIdentity('36d33a59-5398-463a-ac98-0f7d9b201648'); +``` + +## 2. THXAPIClient + +Meant for JavaScript backend applications. + +```javascript +import { THXAPIClient, THXAPIClientOptions } from '@thxnetwork/sdk'; + +const options: THXAPIClientOptions = { + campaignId: '65b0e27845c63cd18e0ab4a6', + clientId: 'msuq4Znuv3q8hLf7ATnlP', + clientSecret: 'YP_k8_LnPG58LHqzWGxg3EMBBGVwwJUmqsuQZdoMtEAD-85hJwRt2vxfev23T92h727bDwCqh3cIkx6meT0xxg', +}; +const thx = new THXAPIClient(options); +``` + +### 2.1 Identities + +Identities are used to connect THX accounts to users in your database. + +```javascript +const identity = await thx.identity.create(); +// 36d33a59-5398-463a-ac98-0f7d9b201648 +``` + +### 2.2 Events + +Events can be used to add requirements for Daily, Invite and Custom Quests. + +```javascript +thx.events.create({ name: 'level_up', identity: '36d33a59-5398-463a-ac98-0f7d9b201648' }); +``` + +### 2.3 Quests + +Quests can be managed programatically. Specify `content` and `contentMetadata` according to the requirements in order to generate proper card previews. + +#### Twitter Post Previews + +Use this `content` and `contentMetadata` for these `interaction` variants: `QuestRequirement.TwitterLike`, `QuestRequirement.TwitterRetweet`, `QuestRequirement.TwitterLikeRetweet`. + +```javascript +const interaction = QuestRequirement.TwitterLikeRetweet; +const content = '46927555'; +const contentMetadata = { + url: 'https://twitter.com/twitter/status/1603121182101970945', + username: 'johndoe', + name: 'John Doe', + text: '✨ Loyalty Networks are here✨ #fintech meets #loyalty', + minAmountFollowers: 123, +}; +``` + +#### Twitter User Previews + +Use this `content` and `contentMetadata` for these `interaction` variants: `QuestRequirement.TwitterFollow`. + +```javascript +const interaction = QuestRequirement.TwitterFollow; +const content = '13241234'; +const contentMetadata = { + id: 46927555, + name: 'John doe', + profileImgUrl: 'https://picsum.com/avatar.jpg', + username: 'johndoe', + minAmountFollowers: 123, +}; +``` + +#### Twitter Message Preview + +Use this `content` and `contentMetadata` for these `interaction` variants: ` QuestRequirement.TwitterMessage`, + +```javascript +const interaction = QuestRequirement.TwitterMessage; +const content = '✨ Loyalty Networks are here✨ #fintech meets #loyalty'; +const contentMetadata = { + minFollowersCount: 123, +}; +``` + +#### Create Twitter Quest + +```javascript +thx.campaigns.quests.create({ + variant: QuestVariant.Twitter, + title: 'Farm along!', + description: 'Get these pointzz...', + amount: 123, + isPublished: true, + interaction, + content, + contentMetadata, +}); +``` + +## 3. THXBrowserClient + +Meant for JavaScript browser applications. + +```javascript +import { THXBrowserClient, THXBrowserClientOptions } from '@thxnetwork/sdk'; + +const options: THXBrowserClientOptions = { + clientId: 'chyBeltL7rmOeTwVu', + clientSecret: 'q4ilZuGA4VPtrGhXug3i5taXrvDtidrzyv-gJN3yVo8T2stL6RwYQjqRoK-iUiAGGvhbG_F3TEFFuD_56Q065Q' + redirectUri: 'https://www.yourdomain.com/auth-callback' + campaignId: '6571c9c6b7d775decb45a8f0', // Optional +}; +const thx = new THXBrowserClient(options); +``` + +Alternatively you can set the `campaignId` at a later moment, for example after obtaining it from a url or database. The campaign is used to scope API requests to a campaign that you own. + +```javascript +thx.setCampaignId('6571c9c6b7d775decb45a8f0'); +``` + +### 3.1 Account + +Get account info for the authenticated user and obtain it's current point balance in your campaign. + +```javascript +// Get Account +await client.account.get(); + +// Update Account +await client.account.patch({ + username: ''; + firstName: ''; + lastName: ''; + email: ''; + profileImg: ''; // Absolute URL +}); + +// Get Point Balance +await client.pointBalance.list(); +``` + +### 3.2 Quests + +List and complete quests in your campaign. + +```javascript +// List Quests +await client.quests.list(); + +// Complete Quests +await client.quests.daily.complete(uuid, { + sub: '', +}); +await client.quests.invite.complete(uuid, { + sub: '', +}); +await client.quests.social.complete(id); +await client.quests.custom.complete(id); +await client.quests.web3.complete(id); +``` + +### 3.3 Rewards + +List and redeem rewards in your campaign. + +```javascript +// List Rewards +await thx.rewards.list(); + +// Get Rewards +await thx.rewards.coin.get(uuid); +await thx.rewards.nft.get(uuid); +await thx.rewards.custom.get(uuid); +await thx.rewards.coupon.get(uuid); + +// Redeem Rewards +await thx.rewards.coin.redemption.post(uuid); +await thx.rewards.nft.redemption.post(uuid); +await thx.rewards.custom.redemption.post(uuid); +await thx.rewards.coupon.redemption.post(uuid); +``` + +### 3.4 Wallet + +List tokens held in your accounts wallet. + +```javascript +await thx.erc20.list({ chainId: 137 }); +await thx.erc721.list({ chainId: 137 }); +await thx.erc1155.list({ chainId: 137 }); +``` diff --git a/libs/sdk/package.json b/libs/sdk/package.json new file mode 100644 index 000000000..b75247741 --- /dev/null +++ b/libs/sdk/package.json @@ -0,0 +1,11 @@ +{ + "name": "sdk", + "version": "0.0.1", + "dependencies": { + "tslib": "^2.3.0" + }, + "type": "commonjs", + "main": "./src/index.js", + "typings": "./src/index.d.ts", + "private": true +} diff --git a/libs/sdk/project.json b/libs/sdk/project.json new file mode 100644 index 000000000..de1d70c52 --- /dev/null +++ b/libs/sdk/project.json @@ -0,0 +1,22 @@ +{ + "name": "sdk", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/sdk/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/sdk", + "main": "libs/sdk/src/index.ts", + "tsConfig": "libs/sdk/tsconfig.lib.json", + "assets": ["libs/sdk/*.md"] + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + } +} diff --git a/libs/sdk/src/index.ts b/libs/sdk/src/index.ts new file mode 100644 index 000000000..1936e4518 --- /dev/null +++ b/libs/sdk/src/index.ts @@ -0,0 +1,3 @@ +export * from './lib/clients'; +export * from './lib/types'; +export * from './lib/types/enums'; diff --git a/libs/sdk/src/lib/clients/API.ts b/libs/sdk/src/lib/clients/API.ts new file mode 100644 index 000000000..e48c59240 --- /dev/null +++ b/libs/sdk/src/lib/clients/API.ts @@ -0,0 +1,22 @@ +import { THXAPIClientOptions } from '../types'; +import { THXOIDCGrant } from '../managers/OIDCManager'; +import RequestManager from '../managers/RequestManager'; +import EventManager from '../managers/EventManager'; +import IdentityManager from '../managers/IdentityManager'; +import CampaignManager from '../managers/CampaignManager'; + +export default class THXAPIClient { + options: THXAPIClientOptions; + request: RequestManager; + identity: IdentityManager; + events: EventManager; + campaigns: CampaignManager; + + constructor(options: THXAPIClientOptions) { + this.options = options; + this.request = new RequestManager(this, THXOIDCGrant.ClientCredentials); + this.identity = new IdentityManager(this); + this.events = new EventManager(this); + this.campaigns = new CampaignManager(this); + } +} diff --git a/libs/sdk/src/lib/clients/Browser.ts b/libs/sdk/src/lib/clients/Browser.ts new file mode 100644 index 000000000..531492a82 --- /dev/null +++ b/libs/sdk/src/lib/clients/Browser.ts @@ -0,0 +1,44 @@ +import ERC20Manager from '../managers/ERC20Manager'; +import ERC721Manager from '../managers/ERC721Manager'; +import ERC1155Manager from '../managers/ERC1155Manager'; +import CouponCodeManager from '../managers/CouponCodeManager'; +import RequestManager from '../managers/RequestManager'; +import AccountManager from '../managers/AccountManager'; +import QuestManager from '../managers/QuestManager'; +import RewardManager from '../managers/RewardManager'; +import QRCodeManager from '../managers/QRCodeManager'; +import PoolManager from '../managers/PoolManager'; +import { THXOIDCGrant } from '../managers/OIDCManager'; +import { THXBrowserClientOptions } from '../types'; + +export default class THXBrowserClient { + options: THXBrowserClientOptions; + request: RequestManager; + account: AccountManager; + erc20: ERC20Manager; + erc721: ERC721Manager; + erc1155: ERC1155Manager; + couponCodes: CouponCodeManager; + quests: QuestManager; + rewards: RewardManager; + qrCodes: QRCodeManager; + pools: PoolManager; + + constructor(options: THXBrowserClientOptions) { + this.options = options; + this.request = new RequestManager(this, THXOIDCGrant.AuthorizationCode); + this.account = new AccountManager(this); + this.erc20 = new ERC20Manager(this); + this.erc721 = new ERC721Manager(this); + this.erc1155 = new ERC1155Manager(this); + this.couponCodes = new CouponCodeManager(this); + this.quests = new QuestManager(this); + this.rewards = new RewardManager(this); + this.qrCodes = new QRCodeManager(this); + this.pools = new PoolManager(this); + } + + setCampaignId(campaignId: string) { + this.options.poolId = campaignId; + } +} diff --git a/libs/sdk/src/lib/clients/Widget.ts b/libs/sdk/src/lib/clients/Widget.ts new file mode 100644 index 000000000..45d89a92b --- /dev/null +++ b/libs/sdk/src/lib/clients/Widget.ts @@ -0,0 +1,27 @@ +import { THXWidgetOptions } from '../types'; + +export default class THXWidget { + static create(options: THXWidgetOptions) { + if (document.getElementById('thx-container')) return; + if (!options) throw new Error("Please provide 'options'."); + + const { campaignId, apiUrl, identity, containerSelector } = options; + if (!campaignId) throw new Error("Please provide 'options.campaignId'."); + + const url = new URL(apiUrl || 'https://api.thx.network'); + url.pathname = `/v1/widget/${campaignId}.js`; + + if (identity) { + url.searchParams.append('identity', identity); + } + + if (containerSelector) { + url.searchParams.append('containerSelector', containerSelector); + } + + const script = document.createElement('script'); + script.src = url.href; + + document.body.appendChild(script); + } +} diff --git a/libs/sdk/src/lib/clients/index.ts b/libs/sdk/src/lib/clients/index.ts new file mode 100644 index 000000000..c39ea201c --- /dev/null +++ b/libs/sdk/src/lib/clients/index.ts @@ -0,0 +1,5 @@ +import THXBrowserClient from './Browser'; +import THXAPIClient from './API'; +import THXWidget from './Widget'; +export type THXClient = THXBrowserClient | THXAPIClient; +export { THXBrowserClient, THXAPIClient, THXWidget }; diff --git a/libs/sdk/src/lib/managers/AccountManager.ts b/libs/sdk/src/lib/managers/AccountManager.ts new file mode 100644 index 000000000..37b1b5d6a --- /dev/null +++ b/libs/sdk/src/lib/managers/AccountManager.ts @@ -0,0 +1,29 @@ +import { THXClient } from '../clients'; +import { ChainId } from '../types/enums/ChainId'; +import BaseManager from './BaseManager'; + +export default class AccountManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async get(chainId?: ChainId) { + return await this.client.request.get('/v1/account' + chainId && `?chainId=${chainId}`); + } + + async patch(body: any) { + return await this.client.request.patch('/v1/account', { data: body }); + } + + wallets = { + list: async () => { + return await this.client.request.get('/v1/account/wallets'); + }, + create: async (body: { chainId: number }) => { + return await this.client.request.post('/v1/account/wallets', { data: body }); + }, + confirm: async (body: { chainId: number }) => { + return await this.client.request.post(`/v1/account/wallets/confirm`, { data: body }); + }, + }; +} diff --git a/libs/sdk/src/lib/managers/BaseManager.ts b/libs/sdk/src/lib/managers/BaseManager.ts new file mode 100644 index 000000000..ac6fb920c --- /dev/null +++ b/libs/sdk/src/lib/managers/BaseManager.ts @@ -0,0 +1,9 @@ +import { THXClient } from '../clients'; + +export default class BaseManager { + client!: THXClient; + + constructor(client: THXClient) { + this.client = client; + } +} diff --git a/libs/sdk/src/lib/managers/CampaignManager.ts b/libs/sdk/src/lib/managers/CampaignManager.ts new file mode 100644 index 000000000..2e5781c48 --- /dev/null +++ b/libs/sdk/src/lib/managers/CampaignManager.ts @@ -0,0 +1,28 @@ +import { THXAPIClient } from '../clients'; +import { THXAPIClientOptions, THXQuestCreateData, THXQuestSocialCreateData } from '../types'; +import BaseManager from './BaseManager'; + +class CampaignManager extends BaseManager { + constructor(client: THXAPIClient) { + super(client); + } + + get(id: string) { + return this.client.request.get(`/v1/pools/${id}`); + } + + quests = { + social: { + create: (data: THXQuestCreateData & THXQuestSocialCreateData) => { + const { campaignId } = this.client.options as THXAPIClientOptions; + const contentMetadata = JSON.stringify(data.contentMetadata); + + return this.client.request.post(`/v1/pools/${campaignId}/quests`, { + data: { ...data, contentMetadata }, + }); + }, + }, + }; +} + +export default CampaignManager; diff --git a/libs/sdk/src/lib/managers/CouponCodeManager.ts b/libs/sdk/src/lib/managers/CouponCodeManager.ts new file mode 100644 index 000000000..5ca5cd1f7 --- /dev/null +++ b/libs/sdk/src/lib/managers/CouponCodeManager.ts @@ -0,0 +1,15 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class CouponCodeManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list(params: { chainId: string }) { + const obj = new URLSearchParams(params); + return await this.client.request.get(`/v1/coupon-rewards/payments?${obj.toString()}`); + } +} + +export default CouponCodeManager; diff --git a/libs/sdk/src/lib/managers/ERC1155Manager.ts b/libs/sdk/src/lib/managers/ERC1155Manager.ts new file mode 100644 index 000000000..039cc8e70 --- /dev/null +++ b/libs/sdk/src/lib/managers/ERC1155Manager.ts @@ -0,0 +1,31 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class ERC1155Manager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list(params: { chainId: string }) { + const obj = new URLSearchParams(params); + return await this.client.request.get(`/v1/erc1155/token?${obj.toString()}`); + } + + async get(id: string) { + return await this.client.request.get(`/v1/erc1155/token/${id}`); + } + + async getContract(id: string) { + return await this.client.request.get(`/v1/erc1155/${id}`); + } + + async getMetadata(erc1155Id: string, metadataId: string) { + return await this.client.request.get(`/v1/erc1155/${erc1155Id}/metadata/${metadataId}`); + } + + async transfer(config: { erc1155Id: string; erc1155TokenId: string; to: string }) { + return await this.client.request.post(`/v1/erc1155/transfer`, { data: JSON.stringify(config) }); + } +} + +export default ERC1155Manager; diff --git a/libs/sdk/src/lib/managers/ERC20Manager.ts b/libs/sdk/src/lib/managers/ERC20Manager.ts new file mode 100644 index 000000000..159d59822 --- /dev/null +++ b/libs/sdk/src/lib/managers/ERC20Manager.ts @@ -0,0 +1,28 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; +import { ChainId } from '../types/enums/ChainId'; + +class ERC20Manager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list(props: { chainId: string }) { + const params = new URLSearchParams(props); + return await this.client.request.get(`/v1/erc20/token?${params.toString()}`); + } + + async get(id: string) { + return await this.client.request.get(`/v1/erc20/token/${id}`); + } + + async getContract(id: string) { + return await this.client.request.get(`/v1/erc20/${id}`); + } + + async transfer(config: { erc20Id: string; to: string; amount: string; chainId: ChainId }) { + return await this.client.request.post(`/v1/erc20/transfer`, { data: JSON.stringify(config) }); + } +} + +export default ERC20Manager; diff --git a/libs/sdk/src/lib/managers/ERC721Manager.ts b/libs/sdk/src/lib/managers/ERC721Manager.ts new file mode 100644 index 000000000..ee4dc22a2 --- /dev/null +++ b/libs/sdk/src/lib/managers/ERC721Manager.ts @@ -0,0 +1,31 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class ERC721Manager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list(params: { chainId: string }) { + const obj = new URLSearchParams(params); + return await this.client.request.get(`/v1/erc721/token?${obj.toString()}`); + } + + async get(id: string) { + return await this.client.request.get(`/v1/erc721/token/${id}`); + } + + async getContract(id: string) { + return await this.client.request.get(`/v1/erc721/${id}`); + } + + async getMetadata(erc721Id: string, metadataId: string) { + return await this.client.request.get(`/v1/erc721/${erc721Id}/metadata/${metadataId}`); + } + + async transfer(config: { erc721Id: string; erc721TokenId: string; to: string }) { + return await this.client.request.post(`/v1/erc721/transfer`, { data: JSON.stringify(config) }); + } +} + +export default ERC721Manager; diff --git a/libs/sdk/src/lib/managers/EventManager.ts b/libs/sdk/src/lib/managers/EventManager.ts new file mode 100644 index 000000000..3cfcca645 --- /dev/null +++ b/libs/sdk/src/lib/managers/EventManager.ts @@ -0,0 +1,28 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class EventManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async create(options: { event: string; identity: string }) { + const { event, identity } = options; + + if (!event) { + throw new Error("Please provide an 'event' parameter."); + } + if (event.length > 50) { + throw new Error("Please provide an 'event' with a string length max 50 characters."); + } + if (!identity) { + throw new Error("Please provide an 'identity' parameter. Create it with 'client.identity.create()'."); + } + + await this.client.request.post('/v1/events', { + data: JSON.stringify({ event, identityUuid: identity }), + }); + } +} + +export default EventManager; diff --git a/libs/sdk/src/lib/managers/IdentityManager.ts b/libs/sdk/src/lib/managers/IdentityManager.ts new file mode 100644 index 000000000..849ac167c --- /dev/null +++ b/libs/sdk/src/lib/managers/IdentityManager.ts @@ -0,0 +1,18 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class IdentityManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + create() { + return this.client.request.post('/v1/identity'); + } + + get(salt: string) { + return this.client.request.get(`/v1/identity/${salt}`); + } +} + +export default IdentityManager; diff --git a/libs/sdk/src/lib/managers/OIDCManager.ts b/libs/sdk/src/lib/managers/OIDCManager.ts new file mode 100644 index 000000000..4da22dca0 --- /dev/null +++ b/libs/sdk/src/lib/managers/OIDCManager.ts @@ -0,0 +1,123 @@ +import axios from 'axios'; +import { THXClient } from '../clients'; +import { THXOIDCConfig, THXOIDCUser } from '../types'; +import BaseManager from './BaseManager'; + +export enum THXOIDCGrant { + AuthorizationCode = 'authorization_code', + ClientCredentials = 'client_credentials', +} + +class OIDCManager extends BaseManager { + user: THXOIDCUser | null; + expiresAt: number; + grantType: THXOIDCGrant; + + constructor(client: THXClient, grantType: THXOIDCGrant) { + super(client); + + this.grantType = grantType; + this.expiresAt = Date.now(); + this.user = null; + } + + get isExpired() { + return Date.now() > this.expiresAt; + } + + get isAuthenticated() { + return this.user && !this.isExpired; + } + + get authUrl() { + return this.client.options.authUrl || 'https://auth.thx.network'; + } + + setUser(user: THXOIDCUser) { + this.user = user; + this.expiresAt = Date.now() + this.user.expires_in * 1000; + } + + getUser() { + return this.user; + } + + async authenticate() { + const initMap = { + [String(THXOIDCGrant.ClientCredentials)]: this.getClientCredentialsGrant.bind(this), + [String(THXOIDCGrant.AuthorizationCode)]: this.getAuthorizationCodeGrant.bind(this), + }; + await initMap[this.grantType](this.client.options); + } + + async redirectCallback() { + // Once user is redirected back to your application with the authorization code + const code = new URL(window.location.href).searchParams.get('code'); + if (!code) throw new Error("Could not find 'code' search param in url."); + + const { clientId, redirectUri } = this.client.options; + const data = { + code, + grant_type: THXOIDCGrant.AuthorizationCode, + client_id: clientId, + redirect_uri: redirectUri, + }; + + try { + const response = await axios(`${this.authUrl}/token`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: JSON.stringify(data), + }); + + this.setUser(response.data); + } catch (error) { + throw new Error('Request for ' + THXOIDCGrant.ClientCredentials + ' grant failed.'); + } + } + + private async getClientCredentialsGrant({ clientId, clientSecret }: THXOIDCConfig) { + const authHeader = 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); + const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': authHeader, + }; + const url = `${this.authUrl}/token`; + const params = new URLSearchParams({ + grant_type: THXOIDCGrant.ClientCredentials, + scope: 'openid events:write identities:read identities:write pools:read pools:write', + }); + + try { + const response = await axios({ + method: 'POST', + url, + headers, + data: params, + }); + + this.setUser(response.data); + } catch (error) { + console.log(error); + throw new Error('Request for ' + THXOIDCGrant.ClientCredentials + ' grant failed.'); + } + } + + private async getAuthorizationCodeGrant({ clientId, redirectUri }: THXOIDCConfig) { + if (!redirectUri) throw new Error("Please, set 'options.redirectUri'."); + + const authorizationEndpoint = this.authUrl + '/authorize'; + const scope = + 'openid offline_access account:read account:write erc20:read erc721:read erc1155:read point_balances:read referral_rewards:read point_rewards:read wallets:read wallets:write pool_subscription:read pool_subscription:write claims:read'; + const authUrl = new URL(authorizationEndpoint); + + authUrl.searchParams.append('client_id', clientId); + authUrl.searchParams.append('redirect_uri', redirectUri); + authUrl.searchParams.append('scope', scope); + authUrl.searchParams.append('response_type', 'code'); + + window.location.href = authUrl.toString(); + } +} + +export default OIDCManager; diff --git a/libs/sdk/src/lib/managers/PointBalanceManager.ts b/libs/sdk/src/lib/managers/PointBalanceManager.ts new file mode 100644 index 000000000..206376a38 --- /dev/null +++ b/libs/sdk/src/lib/managers/PointBalanceManager.ts @@ -0,0 +1,14 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class PointBalanceManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list() { + return await this.client.request.get('/v1/point-balances'); + } +} + +export default PointBalanceManager; diff --git a/libs/sdk/src/lib/managers/PoolManager.ts b/libs/sdk/src/lib/managers/PoolManager.ts new file mode 100644 index 000000000..503f3faa3 --- /dev/null +++ b/libs/sdk/src/lib/managers/PoolManager.ts @@ -0,0 +1,34 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class PoolManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async get(id: string) { + return await this.client.request.get(`/v1/pools/${id}`); + } + + async getLeaderboard(id: string) { + return await this.client.request.get(`/v1/pools/${id}/analytics/leaderboard/client?platform=discord`); + } + + subscription = { + get: async (id: string) => { + return await this.client.request.get(`/v1/pools/${id}/subscription`); + }, + + post: async (payload: { poolId: string; email: string }) => { + return await this.client.request.post(`/v1/pools/${payload.poolId}/subscription`, { + data: JSON.stringify({ email: payload.email }), + }); + }, + + delete: async (id: string) => { + return await this.client.request.delete(`/v1/pools/${id}/subscription`); + }, + }; +} + +export default PoolManager; diff --git a/libs/sdk/src/lib/managers/QRCodeManager.ts b/libs/sdk/src/lib/managers/QRCodeManager.ts new file mode 100644 index 000000000..387748c00 --- /dev/null +++ b/libs/sdk/src/lib/managers/QRCodeManager.ts @@ -0,0 +1,20 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class QRCodeManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + get(uuid: string) { + return this.client.request.get(`/v1/qr-codes/${uuid}`); + } + + entry = { + create: (uuid: string) => { + return this.client.request.post(`/v1/qr-codes/${uuid}/entries`); + }, + }; +} + +export default QRCodeManager; diff --git a/libs/sdk/src/lib/managers/QuestManager.ts b/libs/sdk/src/lib/managers/QuestManager.ts new file mode 100644 index 000000000..b2ce933f6 --- /dev/null +++ b/libs/sdk/src/lib/managers/QuestManager.ts @@ -0,0 +1,68 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class QuestManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list(poolId?: string) { + return await this.client.request.get(`/v1/quests`, { poolId }); + } + + daily = { + entry: { + create: async (id: string) => { + return await this.client.request.post(`/v1/quests/daily/${id}/entries`); + }, + }, + }; + + invite = { + entry: { + create: async (id: string, payload: { sub: string }) => { + return await this.client.request.post(`/v1/quests/invite/${id}/entries`, { + data: JSON.stringify(payload), + }); + }, + }, + }; + + social = { + entry: { + create: async (id: string) => { + return await this.client.request.post(`/v1/quests/social/${id}/entries`); + }, + }, + }; + + custom = { + entry: { + create: async (id: string) => { + return await this.client.request.post(`/v1/quests/custom/${id}/entries`); + }, + }, + }; + + web3 = { + entry: { + create: async (id: string, payload: { signature: string; message: string }) => { + return await this.client.request.post(`/v1/quests/web3/${id}/entries`, { + data: JSON.stringify(payload), + }); + }, + }, + }; + + gitcoin = { + entry: { + create: async (id: string, payload: { signature: string; message: string }) => { + return await this.client.request.post(`/v1/quests/gitcoin/${id}/entries`, { + data: JSON.stringify(payload), + }); + }, + }, + }; +} + +export default QuestManager; diff --git a/libs/sdk/src/lib/managers/RequestManager.ts b/libs/sdk/src/lib/managers/RequestManager.ts new file mode 100644 index 000000000..ec25d9653 --- /dev/null +++ b/libs/sdk/src/lib/managers/RequestManager.ts @@ -0,0 +1,104 @@ +import { THXClient } from '../clients'; +import { THXBrowserClientOptions, THXRequestConfig } from '../types'; +import * as jose from 'jose'; +import axios, { AxiosError, AxiosResponse } from 'axios'; +import OIDCManager, { THXOIDCGrant } from './OIDCManager'; + +class RequestManager extends OIDCManager { + constructor(client: THXClient, grantType: THXOIDCGrant) { + super(client, grantType); + } + + get(path: string, config?: THXRequestConfig) { + return this.request(path, { ...config, method: 'GET' }); + } + + post(path: string, config?: THXRequestConfig) { + return this.request(path, { ...config, method: 'POST' }); + } + + patch(path: string, config?: THXRequestConfig) { + return this.request(path, { ...config, method: 'PATCH' }); + } + + put(path: string, config?: THXRequestConfig) { + return this.request(path, { ...config, method: 'PUT' }); + } + + delete(path: string, config?: THXRequestConfig) { + return this.request(path, { ...config, method: 'DELETE' }); + } + + get apiUrl() { + return this.client.options.apiUrl || 'https://api.thx.network'; + } + + private async request(path: string, config: THXRequestConfig) { + // Check for user to exist and token not to be expired if auth is intended + const { clientId, clientSecret } = this.client.options; + if (!this.isAuthenticated && clientId && clientSecret) { + await this.authenticate(); + } + + const headers = this.getHeaders(config); + const url = `${this.apiUrl}${path}`; + try { + const response = await axios({ ...config, url, headers }); + + return await this.handleResponse(response); + } catch (error) { + return await this.handleError(error as AxiosError); + } + } + + private getHeaders(config?: THXRequestConfig) { + const headers: Record<string, string> = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }; + + if (this.user && this.user.access_token) { + const token = this.validateToken(this.user.access_token); + headers['Authorization'] = `Bearer ${token}`; + } + + // Needs refactor in places where X-PoolId is as config.poolId or config header + const options = this.client.options as THXBrowserClientOptions; + if ((config && config.poolId) || (options && options.poolId)) { + headers['X-PoolId'] = (config && config.poolId) || (options && options.poolId); + } + + return { ...headers, ...config?.headers }; + } + + private async handleResponse(r: AxiosResponse) { + // Return response data, but throw if HTTP status with the range of 400 - 600 + if (r.status >= 400 && r.status < 600) { + throw r.data; + } else { + return r.data; + } + } + + private async handleError(error: AxiosError) { + if (!error || !error.response) throw new Error('Could not parse failed response.'); + throw error.response.data; + } + + private validateToken(accessToken: string) { + if (!accessToken) throw new Error("Please, provide an 'accessToken'."); + + try { + const { exp } = jose.decodeJwt(accessToken); + if (!exp || Date.now() > Number(exp) * 1000) { + throw new Error('The token has expired.'); + } + + return accessToken; + } catch (error) { + throw new Error("Failed to validate this 'accessToken'."); + } + } +} + +export default RequestManager; diff --git a/libs/sdk/src/lib/managers/RewardManager.ts b/libs/sdk/src/lib/managers/RewardManager.ts new file mode 100644 index 000000000..08c358fdb --- /dev/null +++ b/libs/sdk/src/lib/managers/RewardManager.ts @@ -0,0 +1,73 @@ +import { THXClient } from '../clients'; +import BaseManager from './BaseManager'; + +class RewardManager extends BaseManager { + constructor(client: THXClient) { + super(client); + } + + async list(poolId?: string) { + return await this.client.request.get(`/v1/rewards`, { poolId }); + } + + coin = { + get: async (uuid: string) => { + return await this.client.request.get(`/v1/rewards/coin/${uuid}`); + }, + redemption: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/coin/${uuid}/redemption`); + }, + }, + }; + + nft = { + get: async (uuid: string) => { + return await this.client.request.get(`/v1/rewards/nft/${uuid}`); + }, + redemption: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/nft/${uuid}/redemption`); + }, + }, + payment: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/nft/${uuid}/payment`); + }, + }, + }; + + custom = { + get: async (uuid: string) => { + return await this.client.request.get(`/v1/rewards/custom/${uuid}`); + }, + redemption: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/custom/${uuid}/redemption`); + }, + }, + payment: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/custom/${uuid}/payment`); + }, + }, + }; + + coupon = { + get: async (uuid: string) => { + return await this.client.request.get(`/v1/rewards/coupon/${uuid}`); + }, + redemption: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/coupon/${uuid}/redemption`); + }, + }, + payment: { + post: async (uuid: string) => { + return await this.client.request.post(`/v1/rewards/coupon/${uuid}/payment`); + }, + }, + }; +} + +export default RewardManager; diff --git a/libs/sdk/src/lib/types/enums/ChainId.ts b/libs/sdk/src/lib/types/enums/ChainId.ts new file mode 100644 index 000000000..bac2d4548 --- /dev/null +++ b/libs/sdk/src/lib/types/enums/ChainId.ts @@ -0,0 +1,10 @@ +export enum ChainId { + Ethereum = 1, + Arbitrum = 42161, + BNBChain = 56, + Hardhat = 31337, + PolygonMumbai = 80001, + Polygon = 137, + PolygonZK = 1101, + Linea = 59144, +} diff --git a/libs/sdk/src/lib/types/enums/Quests.ts b/libs/sdk/src/lib/types/enums/Quests.ts new file mode 100644 index 000000000..24c33349e --- /dev/null +++ b/libs/sdk/src/lib/types/enums/Quests.ts @@ -0,0 +1,24 @@ +export enum QuestRequirement { + YouTubeLike = 0, + YouTubeSubscribe = 1, + TwitterLike = 2, + TwitterRetweet = 3, + TwitterFollow = 4, + DiscordGuildJoined = 5, + TwitterQuery = 6, + TwitterLikeRetweet = 7, + DiscordMessage = 8, + DiscordMessageReaction = 9, +} + +export enum QuestVariant { + Daily = 0, + Invite = 1, + Twitter = 2, + Discord = 3, + YouTube = 4, + Custom = 5, + Web3 = 6, + Gitcoin = 7, + Webhook = 8, +} diff --git a/libs/sdk/src/lib/types/enums/index.ts b/libs/sdk/src/lib/types/enums/index.ts new file mode 100644 index 000000000..552effb64 --- /dev/null +++ b/libs/sdk/src/lib/types/enums/index.ts @@ -0,0 +1,2 @@ +export * from './ChainId'; +export * from './Quests'; diff --git a/libs/sdk/src/lib/types/index.ts b/libs/sdk/src/lib/types/index.ts new file mode 100644 index 000000000..90f25ec88 --- /dev/null +++ b/libs/sdk/src/lib/types/index.ts @@ -0,0 +1,93 @@ +import { AxiosRequestConfig } from 'axios'; +import { QuestVariant } from './enums'; + +type THXQuestSocialYouTubePreviewData = { + // +}; + +type THXQuestSocialDiscordPreviewData = { + serverId: string; + inviteURL: string; + limit: number; + days: number; + channels: number; +}; + +type THXQuestSocialTwitterPreviewData = { + minFollowersCount: number; + url: string; + username: string; + name: string; + text: string; + id: string; + profileImgUrl: string; +}; + +type THXQuestSocialCreateData = { + amount: number; + interaction: number; + content: string; + contentMetadata: + | Partial<THXQuestSocialTwitterPreviewData> + | Partial<THXQuestSocialDiscordPreviewData> + | Partial<THXQuestSocialYouTubePreviewData>; +}; + +type THXQuestCreateData = { + variant: QuestVariant; + isPublished: boolean; + title: string; + description?: string; + image?: string; + expiryDate?: Date; +}; + +type THXAPIClientOptions = { + apiUrl?: string; + campaignId?: string; +} & THXOIDCConfig; + +type THXBrowserClientOptions = { + apiUrl?: string; + poolId: string; +} & THXOIDCConfig; + +type THXRequestConfig = { + poolId?: string; +} & AxiosRequestConfig; + +type THXOIDCConfig = { + authUrl?: string; + clientId: string; + clientSecret: string; + redirectUri?: string; +}; + +type THXOIDCUser = { + access_token: string; + expires_in: number; + token_type: string; + scope: string; +}; + +type THXWidgetOptions = { + campaignId?: string; + poolId?: string; + apiUrl?: string; + identity?: string; + containerSelector?: string; +}; + +export type { + THXQuestSocialYouTubePreviewData, + THXQuestSocialDiscordPreviewData, + THXQuestSocialTwitterPreviewData, + THXQuestCreateData, + THXQuestSocialCreateData, + THXWidgetOptions, + THXAPIClientOptions, + THXBrowserClientOptions, + THXOIDCConfig, + THXOIDCUser, + THXRequestConfig, +}; diff --git a/libs/sdk/tsconfig.json b/libs/sdk/tsconfig.json new file mode 100644 index 000000000..62a328e94 --- /dev/null +++ b/libs/sdk/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/libs/sdk/tsconfig.lib.json b/libs/sdk/tsconfig.lib.json new file mode 100644 index 000000000..1912016fb --- /dev/null +++ b/libs/sdk/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": [] +} diff --git a/migrations.json b/migrations.json deleted file mode 100644 index f172bd2cf..000000000 --- a/migrations.json +++ /dev/null @@ -1,334 +0,0 @@ -{ - "migrations": [ - { - "version": "15.7.0-beta.0", - "description": "Split global configuration files into individual project.json files. This migration has been added automatically to the beginning of your migration set to retroactively make them work with the new version of Nx.", - "cli": "nx", - "implementation": "./src/migrations/update-15-7-0/split-configuration-into-project-json-files", - "package": "@nx/workspace", - "name": "15-7-0-split-configuration-into-project-json-files" - }, - { - "cli": "nx", - "version": "15.0.0-beta.1", - "description": "Replace implicitDependencies with namedInputs + target inputs", - "implementation": "./src/migrations/update-15-0-0/migrate-to-inputs", - "package": "nx", - "name": "15.0.0-migrate-to-inputs" - }, - { - "cli": "nx", - "version": "15.0.0-beta.1", - "description": "Prefix outputs with {workspaceRoot}/{projectRoot} if needed", - "implementation": "./src/migrations/update-15-0-0/prefix-outputs", - "package": "nx", - "name": "15.0.0-prefix-outputs" - }, - { - "cli": "nx", - "version": "15.0.12-beta.1", - "description": "Set project names in project.json files", - "implementation": "./src/migrations/update-15-1-0/set-project-names", - "package": "nx", - "name": "15.1.0-set-project-names" - }, - { - "cli": "nx", - "version": "15.8.2-beta.0", - "description": "Updates the nx wrapper.", - "implementation": "./src/migrations/update-15-8-2/update-nxw", - "package": "nx", - "name": "15.8.2-update-nx-wrapper" - }, - { - "cli": "nx", - "version": "16.0.0-beta.0", - "description": "Remove @nrwl/cli.", - "implementation": "./src/migrations/update-16-0-0/remove-nrwl-cli", - "package": "nx", - "name": "16.0.0-remove-nrwl-cli" - }, - { - "cli": "nx", - "version": "16.0.0-beta.9", - "description": "Replace `dependsOn.projects` and `inputs` definitions with new configuration format.", - "implementation": "./src/migrations/update-16-0-0/update-depends-on-to-tokens", - "package": "nx", - "name": "16.0.0-tokens-for-depends-on" - }, - { - "cli": "nx", - "version": "16.0.0-beta.0", - "description": "Replace @nrwl/nx-cloud with nx-cloud", - "implementation": "./src/migrations/update-16-0-0/update-nx-cloud-runner", - "package": "nx", - "name": "16.0.0-update-nx-cloud-runner" - }, - { - "cli": "nx", - "version": "16.2.0-beta.0", - "description": "Remove outputPath from run commands", - "implementation": "./src/migrations/update-16-2-0/remove-run-commands-output-path", - "package": "nx", - "name": "16.2.0-remove-output-path-from-run-commands" - }, - { - "cli": "nx", - "version": "16.6.0-beta.6", - "description": "Prefix outputs with {workspaceRoot}/{projectRoot} if needed", - "implementation": "./src/migrations/update-15-0-0/prefix-outputs", - "package": "nx", - "name": "16.6.0-prefix-outputs" - }, - { - "cli": "nx", - "version": "16.8.0-beta.3", - "description": "Escape $ in env variables", - "implementation": "./src/migrations/update-16-8-0/escape-dollar-sign-env-variables", - "package": "nx", - "name": "16.8.0-escape-dollar-sign-env" - }, - { - "cli": "nx", - "version": "17.0.0-beta.1", - "description": "Updates the default cache directory to .nx/cache", - "implementation": "./src/migrations/update-17-0-0/move-cache-directory", - "package": "nx", - "name": "17.0.0-move-cache-directory" - }, - { - "cli": "nx", - "version": "17.0.0-beta.3", - "description": "Use minimal config for tasksRunnerOptions", - "implementation": "./src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options", - "package": "nx", - "name": "17.0.0-use-minimal-config-for-tasks-runner-options" - }, - { - "version": "17.0.0-rc.1", - "description": "Migration for v17.0.0-rc.1", - "implementation": "./src/migrations/update-17-0-0/rm-default-collection-npm-scope", - "package": "nx", - "name": "rm-default-collection-npm-scope" - }, - { - "cli": "nx", - "version": "17.3.0-beta.6", - "description": "Updates the nx wrapper.", - "implementation": "./src/migrations/update-17-3-0/update-nxw", - "package": "nx", - "name": "17.3.0-update-nx-wrapper" - }, - { - "cli": "nx", - "version": "18.0.0-beta.2", - "description": "Updates .env to disabled adding plugins when generating projects in an existing Nx workspace", - "implementation": "./src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces", - "x-repair-skip": true, - "package": "nx", - "name": "18.0.0-disable-adding-plugins-for-existing-workspaces" - }, - { - "version": "15.7.0-beta.0", - "description": "Split global configuration files (e.g., workspace.json) into individual project.json files.", - "cli": "nx", - "implementation": "./src/migrations/update-15-7-0/split-configuration-into-project-json-files", - "package": "@nx/workspace", - "name": "15-7-0-split-configuration-into-project-json-files" - }, - { - "cli": "nx", - "version": "16.0.0-beta.1", - "description": "Replace @nx/workspace with @nx/workspace", - "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages", - "package": "@nx/workspace", - "name": "update-16-0-0-add-nx-packages" - }, - { - "version": "16.0.0-beta.4", - "description": "Generates a plugin called 'workspace-plugin' containing your workspace generators.", - "cli": "nx", - "implementation": "./src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin", - "package": "@nx/workspace", - "name": "16-0-0-move-workspace-generators-into-local-plugin" - }, - { - "version": "16.0.0-beta.9", - "description": "Fix .babelrc presets if it contains an invalid entry for @nx/web/babel.", - "cli": "nx", - "implementation": "./src/migrations/update-16-0-0/fix-invalid-babelrc", - "package": "@nx/workspace", - "name": "16-0-0-fix-invalid-babelrc" - }, - { - "cli": "nx", - "version": "16.0.0-beta.1", - "description": "Replace @nx/eslint-plugin with @nx/eslint-plugin", - "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages", - "package": "@nx/eslint-plugin", - "name": "update-16-0-0-add-nx-packages" - }, - { - "cli": "nx", - "version": "17.2.6-beta.1", - "description": "Rename workspace rules from @nx/workspace/name to @nx/workspace-name", - "implementation": "./src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules", - "package": "@nx/eslint-plugin", - "name": "update-17-2-6-rename-workspace-rules" - }, - { - "cli": "nx", - "version": "15.0.0-beta.0", - "description": "Stop hashing eslint config files for build targets and dependent tasks", - "factory": "./src/migrations/update-15-0-0/add-eslint-inputs", - "package": "@nx/eslint", - "name": "add-eslint-inputs" - }, - { - "cli": "nx", - "version": "15.7.1-beta.0", - "description": "Add node_modules to root eslint ignore", - "factory": "./src/migrations/update-15-7-1/add-eslint-ignore", - "package": "@nx/eslint", - "name": "add-eslint-ignore" - }, - { - "cli": "nx", - "version": "16.0.0-beta.1", - "description": "Replace @nx/eslint with @nx/eslint", - "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages", - "package": "@nx/eslint", - "name": "update-16-0-0-add-nx-packages" - }, - { - "version": "16.8.0", - "description": "update-16-8-0-add-ignored-files", - "implementation": "./src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files", - "package": "@nx/eslint", - "name": "update-16-8-0-add-ignored-files" - }, - { - "version": "17.0.0-beta.7", - "description": "update-17-0-0-rename-to-eslint", - "implementation": "./src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint", - "package": "@nx/eslint", - "name": "update-17-0-0-rename-to-eslint" - }, - { - "version": "17.1.0-beta.1", - "description": "Updates for @typescript-utils/utils v6.9.1+", - "implementation": "./src/migrations/update-17-1-0/update-typescript-eslint", - "package": "@nx/eslint", - "name": "update-typescript-eslint" - }, - { - "version": "17.2.0-beta.0", - "description": "Simplify eslintFilePatterns", - "implementation": "./src/migrations/update-17-2-0/simplify-eslint-patterns", - "package": "@nx/eslint", - "name": "simplify-eslint-patterns" - }, - { - "version": "17.2.9", - "description": "Move executor options to target defaults", - "implementation": "./src/migrations/update-17-2-9/move-options-to-target-defaults", - "package": "@nx/eslint", - "name": "move-options-to-target-defaults" - }, - { - "cli": "nx", - "version": "15.1.0-beta.0", - "description": "Update to Cypress v11. This migration will only update if the workspace is already on v10. https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/", - "factory": "./src/migrations/update-15-1-0/cypress-11", - "package": "@nx/cypress", - "name": "update-to-cypress-11" - }, - { - "cli": "nx", - "version": "15.5.0-beta.0", - "description": "Update to Cypress v12. Cypress 12 contains a handful of breaking changes that might causes tests to start failing that nx cannot directly fix. Read more Cypress 12 changes: https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-12-0.This migration will only run if you are already using Cypress v11.", - "factory": "./src/migrations/update-15-5-0/update-to-cypress-12", - "package": "@nx/cypress", - "name": "update-to-cypress-12" - }, - { - "cli": "nx", - "version": "16.0.0-beta.1", - "description": "Replace @nx/cypress with @nx/cypress", - "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages", - "package": "@nx/cypress", - "name": "update-16-0-0-add-nx-packages" - }, - { - "cli": "nx", - "version": "16.2.0-beta.0", - "description": "Normalize tsconfig.cy.json files to be located at '<projectRoot>/cypress/tsconfig.json'", - "implementation": "./src/migrations/update-16-2-0/update-cy-tsconfig", - "package": "@nx/cypress", - "name": "update-16-2-0-normalize-tsconfigs" - }, - { - "cli": "nx", - "version": "16.4.0-beta.10", - "description": "Remove tsconfig.e2e.json and add settings to project tsconfig.json. tsConfigs executor option is now deprecated. The project level tsconfig.json file should be used instead.", - "implementation": "./src/migrations/update-16-4-0/tsconfig-sourcemaps", - "package": "@nx/cypress", - "name": "update-16-3-0-remove-old-tsconfigs" - }, - { - "cli": "nx", - "version": "16.8.0-beta.4", - "description": "Update to Cypress v13. Most noteable change is video recording is off by default. This migration will only update if the workspace is already on Cypress v12. https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-130", - "implementation": "./src/migrations/update-16-8-0/cypress-13", - "package": "@nx/cypress", - "name": "update-16-8-0-cypress-13" - }, - { - "version": "15.0.0-beta.0", - "cli": "nx", - "description": "Stop hashing jest spec files and config files for build targets and dependent tasks", - "factory": "./src/migrations/update-15-0-0/add-jest-inputs", - "package": "@nx/jest", - "name": "add-jest-inputs" - }, - { - "version": "15.8.0-beta.0", - "cli": "nx", - "description": "Update jest configs to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)", - "factory": "./src/migrations/update-15-8-0/update-configs-jest-29", - "package": "@nx/jest", - "name": "update-configs-jest-29" - }, - { - "version": "15.8.0-beta.0", - "cli": "nx", - "description": "Update jest test files to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)", - "factory": "./src/migrations/update-15-8-0/update-tests-jest-29", - "package": "@nx/jest", - "name": "update-tests-jest-29" - }, - { - "cli": "nx", - "version": "16.0.0-beta.1", - "description": "Replace @nx/jest with @nx/jest", - "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages", - "package": "@nx/jest", - "name": "update-16-0-0-add-nx-packages" - }, - { - "cli": "nx", - "version": "16.5.0-beta.2", - "description": "Add test-setup.ts to ignored files in production input", - "implementation": "./src/migrations/update-16-5-0/add-test-setup-to-inputs-ignore", - "package": "@nx/jest", - "name": "add-test-setup-to-inputs-ignore" - }, - { - "version": "17.1.0-beta.2", - "description": "Move jest executor options to nx.json targetDefaults", - "implementation": "./src/migrations/update-17-1-0/move-options-to-target-defaults", - "package": "@nx/jest", - "name": "move-options-to-target-defaults" - } - ] -} diff --git a/newrelic.js b/newrelic.js new file mode 100644 index 000000000..4e76b4b3e --- /dev/null +++ b/newrelic.js @@ -0,0 +1,36 @@ +'use strict'; +/** + * New Relic agent configuration. + * + * See lib/config/default.js in the agent distribution for a more complete + * description of configuration variables and their potential values. + * https://docs.newrelic.com/docs/apm/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration/ + * + * Please be aware this file is shared between API and AUTH projects. Since this file mostly serves to disable + * New Relic on local runs and configuration during production runs is done through env vars this should not + * lead to issues. + */ +exports.config = { + agent_enabled: false, + logging: { + enabled: false, + }, + rules: { + ignore: ['v1/ping'], + }, + allow_all_headers: true, + attributes: { + exclude: [ + 'request.headers.cookie', + 'request.headers.authorization', + 'request.headers.proxyAuthorization', + 'request.headers.setCookie*', + 'request.headers.x*', + 'response.headers.cookie', + 'response.headers.authorization', + 'response.headers.proxyAuthorization', + 'response.headers.setCookie*', + 'response.headers.x*', + ], + }, +}; diff --git a/nx.json b/nx.json index 663d93427..c9b3d5ee1 100644 --- a/nx.json +++ b/nx.json @@ -1,8 +1,5 @@ { "$schema": "./node_modules/nx/schemas/nx-schema.json", - "affected": { - "defaultBase": "main" - }, "targetDefaults": { "build": { "dependsOn": ["^build"], @@ -37,6 +34,16 @@ "e2e": { "cache": true, "inputs": ["default", "^production"] + }, + "@nx/js:tsc": { + "cache": true, + "dependsOn": ["^build"], + "inputs": ["production", "^production"] + }, + "@nx/webpack:webpack": { + "cache": true, + "dependsOn": ["^build"], + "inputs": ["production", "^production"] } }, "namedInputs": { @@ -64,7 +71,7 @@ } } ], - "defaultProject": "campaign", + "defaultProject": "app", "generators": { "@nx/web:application": { "style": "scss", @@ -72,5 +79,6 @@ "unitTestRunner": "vitest", "e2eTestRunner": "playwright" } - } + }, + "defaultBase": "main" } diff --git a/package.json b/package.json index ad35e6cb6..1fee500d6 100644 --- a/package.json +++ b/package.json @@ -5,44 +5,89 @@ "scripts": { "start": "nx serve", "build": "nx build", - "test": "nx test", - "serve:app": "nx serve campaign" + "build:safe": "docker compose exec txs-web python manage.py insert_safe_master_copy --address \"0xd916a690676e925Ac9Faf2d01869c13Fd9757ef2\"", + "ngrok": "~/ngrok http --domain=local.auth.thx.network https://localhost:3030 > /dev/null &", + "hardhat": "nx run api:hardhat", + "hardhat-deploy": "nx run api:hardhat-deploy", + "test": "NODE_OPTIONS='--max-old-space-size=8192' nx test", + "serve:docker": "docker-compose --env-file .env.example -f docker-compose.yml -f docker-compose.safe.yml up --build -d", + "serve:app": "nx serve app", + "serve:api": "NODE_TLS_REJECT_UNAUTHORIZED='0' nx serve api", + "serve:auth": "NODE_TLS_REJECT_UNAUTHORIZED='0' nx serve auth" }, "private": true, "dependencies": { + "@aws-sdk/client-s3": "^3.576.0", + "@aws-sdk/client-ses": "^3.576.0", "@balancer-labs/sdk": "^1.1.5", "@balancer/sdk": "^0.14.0", "@ethersproject/wallet": "^5.7.0", + "@gala-chain/client": "^1.1.20", + "@godaddy/terminus": "^4.12.1", + "@hokify/agenda": "^6.3.0", + "@hubspot/api-client": "^11.1.0", + "@nomicfoundation/hardhat-toolbox": "2.0.2", + "@openzeppelin/contracts": "3.4.2", + "@openzeppelin/defender-relay-client": "^1.54.2", + "@pinata/sdk": "^2.1.0", "@popperjs/core": "^2.11.6", "@safe-global/api-kit": "^1.1.0", - "@safe-global/protocol-kit": "^1.0.1", + "@safe-global/protocol-kit": "1.2.0", "@safe-global/safe-core-sdk-types": "^1.9.2", "@sentry/vue": "^7.71.0", - "@thxnetwork/sdk": "1.3.5", "@tkey/default": "10.1.0", "@tkey/security-questions": "10.1.0", "@tkey/web-storage": "10.1.0", + "@types/multer": "^1.4.11", + "@types/oidc-provider": "^7.14.0", "@vuepic/vue-datepicker": "7.2.0", "@wagmi/connectors": "^4.1.14", "@wagmi/core": "^2.6.5", "@walletconnect/modal": "^2.6.2", "@web3modal/wagmi": "^4.0.9", + "alchemy-sdk": "^3.3.1", "axios": "^1.6.5", + "axios-better-stacktrace": "^2.1.6", + "bcrypt": "^5.1.1", "bootstrap": "5.3", "bootstrap-vue-next": "^0.14.10", "chart.js": "^4.4.0", "color": "^4.2.3", "core-js": "^3.6.5", "date-fns": "^2.29.3", + "discord.js": "^14.15.2", "ethers": "5.7.2", + "express": "~4.18.1", + "express-async-errors": "^3.1.1", + "express-ejs-layouts": "^2.5.1", + "express-jwt": "^8.4.1", + "express-jwt-permissions": "^1.3.7", + "express-rate-limit": "^7.2.0", + "express-validator": "^7.0.1", + "fabric-ca-client": "^2.2.20", + "fabric-network": "^2.2.20", + "googleapis": "^137.1.0", + "helmet": "^7.1.0", "jose": "^4.14.4", + "jszip": "^3.10.1", + "jwks-rsa": "^3.1.0", + "lusca": "^1.7.0", + "magic-bytes.js": "^1.10.0", "marked": "^12.0.2", "mixpanel-browser": "^2.45.0", + "mongoose": "^8.3.5", + "morgan": "^1.10.0", + "morgan-body": "^2.6.9", + "multer": "^1.4.5-lts.1", + "newrelic": "^11.17.0", "oidc-client-ts": "^2.2.4", + "oidc-provider": "7.14.3", "pinia": "^2.0.23", "promise-poller": "^1.9.1", "protobufjs": "^7.2.4", + "short-uuid": "^5.2.0", "tslib": "^2.3.0", + "unique-username-generator": "^1.3.0", "viem": "^2.7.13", "vue": "^3.3.4", "vue-chart-3": "^3.1.8", @@ -51,35 +96,59 @@ "vue3-clipboard": "^1.0.0", "vue3-toastify": "^0.1.11", "web3-utils": "^1.8.2", + "winston": "^3.13.0", "yarn": "^1.22.21" }, "devDependencies": { + "@ethersproject/abi": "^5.4.7", + "@ethersproject/providers": "^5.4.7", + "@gnosis.pm/safe-contracts": "1.3.0", + "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomiclabs/hardhat-ethers": "^2.0.0", + "@nomiclabs/hardhat-etherscan": "^3.0.0", + "@nrwl/node": "^19.0.4", "@nx-plus/vue": "^14.1.0", - "@nx/cypress": "18.0.8", - "@nx/devkit": "18.0.8", - "@nx/eslint": "18.0.8", - "@nx/eslint-plugin": "18.0.8", - "@nx/jest": "18.0.8", - "@nx/js": "18.0.8", - "@nx/playwright": "18.0.8", - "@nx/vite": "18.0.8", - "@nx/web": "18.0.8", - "@nx/workspace": "18.0.8", + "@nx/cypress": "18.3.0", + "@nx/devkit": "18.3.0", + "@nx/esbuild": "18.3.0", + "@nx/eslint": "18.3.0", + "@nx/eslint-plugin": "18.3.0", + "@nx/jest": "18.3.0", + "@nx/js": "18.3.0", + "@nx/node": "18.3.0", + "@nx/playwright": "18.3.0", + "@nx/vite": "18.3.0", + "@nx/web": "18.3.0", + "@nx/webpack": "18.3.0", + "@nx/workspace": "18.3.0", "@playwright/test": "^1.36.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", "@sentry/vite-plugin": "^2.7.1", + "@svgr/webpack": "^8.0.1", "@swc-node/register": "~1.8.0", "@swc/core": "~1.3.85", "@swc/helpers": "~0.5.2", + "@typechain/ethers-v5": "^10.1.0", + "@typechain/hardhat": "^6.1.2", + "@types/chai": "^4.2.0", "@types/color": "^3.0.3", + "@types/compression": "^1.7.5", + "@types/ejs": "^3.1.5", + "@types/express": "~4.17.13", "@types/jest": "29.4.4", + "@types/lusca": "^1.7.5", + "@types/migrate-mongo": "^10.0.4", "@types/mixpanel-browser": "^2.38.0", - "@types/node": "18.16.9", + "@types/mocha": ">=9.1.0", + "@types/node": "~18.16.9", + "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.1", - "@typescript-eslint/eslint-plugin": "6.21.0", - "@typescript-eslint/parser": "6.21.0", + "@typescript-eslint/eslint-plugin": "7.9.0", + "@typescript-eslint/parser": "7.9.0", "@vitejs/plugin-vue": "^5.0.4", - "@vitest/coverage-v8": "^1.0.4", - "@vitest/ui": "^1.3.1", + "@vitest/coverage-v8": "1.6.0", + "@vitest/ui": "1.6.0", "@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-typescript": "~4.5.0", "@vue/cli-service": "~4.5.0", @@ -88,35 +157,47 @@ "@vue/eslint-config-typescript": "^5.0.2", "@vue/test-utils": "^2.0.0-0", "c8": "^7.12.0", - "cypress": "13.6.4", - "eslint": "8.48.0", + "canvas": "^2.11.2", + "chai": "^4.2.0", + "cypress": "^13.6.6", + "esbuild": "^0.19.2", + "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-cypress": "2.15.1", "eslint-plugin-import": "^2.26.0", "eslint-plugin-playwright": "^0.15.3", "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-vue": "^7.0.0-0", + "hardhat": "2.14.0", + "hardhat-gas-reporter": "^1.0.8", "jest": "29.4.3", "jest-environment-jsdom": "29.4.3", + "jest-environment-node": "^29.4.1", "jest-serializer-vue": "^2.0.2", "jest-transform-stub": "^2.0.0", "jsdom": "~22.1.0", - "nx": "18.0.8", + "migrate-mongo": "^11.0.0", + "nock": "^13.5.4", + "nx": "18.3.0", "prettier": "^2.6.2", + "react-refresh": "^0.10.0", + "solidity-coverage": "^0.8.0", + "supertest": "^7.0.0", "swc-loader": "0.1.15", "ts-jest": "29.1.2", "ts-node": "10.9.1", - "typescript": "^5.3.3", + "typechain": "^8.3.0", + "typescript": "5.4.5", "unplugin-vue-components": "^0.25.1", "vite": "5.1.6", "vite-plugin-eslint": "^1.6.0", "vite-plugin-mkcert": "^1.16.0", "vite-plugin-node-polyfills": "^0.9.0", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.3.1", + "vitest": "1.6.0", "vue3-jest": "^27.0.0-alpha.1" }, "resolutions": { - "@achrinza/node-ipc": "9.2.5" + "@achrinza/node-ipc": "9.2.7" } } diff --git a/scripts/insert_safe_master_copy.py b/scripts/insert_safe_master_copy.py new file mode 100644 index 000000000..ad0ca80bf --- /dev/null +++ b/scripts/insert_safe_master_copy.py @@ -0,0 +1,48 @@ +import binascii + +from django.core.management.base import BaseCommand +from ...models import SafeMasterCopy + +class Command(BaseCommand): + help = "Insert SafeMasterCopy objects" + + def add_arguments(self, parser): + parser.add_argument("--address", help="SafeMasterCopy address", required=True) + parser.add_argument("--initial-block-number", help="Initial block number", required=False, default=0) + parser.add_argument("--tx-block-number", help="Transaction block number", required=False, default=None) + parser.add_argument("--safe-version", help="Safe Version", required=False, default="1.3.0") + parser.add_argument("--l2", help="Address on L2", required=False, default=True) + parser.add_argument("--deployer", help="Deployer", required=False, default="Gnosis") + + def handle(self, *args, **options): + mastercopy_address = options["address"] + initial_block_number = options["initial_block_number"] + tx_block_number = options["tx_block_number"] + version = options["safe_version"] + l2 = options["l2"] + deployer = options["deployer"] + + # Convert the mastercopy address to binary + address = self.ethereum_address_to_binary(mastercopy_address) + + # Create SafeMasterCopy object + SafeMasterCopy.objects.create( + address=address, + initial_block_number=initial_block_number, + tx_block_number=tx_block_number, + version=version, + l2=l2, + deployer=deployer, + ) + + self.stdout.write(self.style.SUCCESS(f"Created SafeMasterCopy for {mastercopy_address}")) + + def ethereum_address_to_binary(self, address): + # Remove the '0x' prefix from the address if it exists + if address.startswith('0x'): + address = address[2:] + + # Convert the hexadecimal string to binary representation + binary_representation = binascii.unhexlify(address) + + return binary_representation \ No newline at end of file diff --git a/scripts/mongo-init.sh b/scripts/mongo-init.sh new file mode 100644 index 000000000..7ec4f46be --- /dev/null +++ b/scripts/mongo-init.sh @@ -0,0 +1,4 @@ +mongoimport --uri="mongodb://$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@localhost:27017/auth?authSource=admin&ssl=false" --file="/docker-entrypoint-initdb.d/fixture/client.json" --jsonArray; +mongoimport --uri="mongodb://$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@localhost:27017/auth?authSource=admin&ssl=false" --file="/docker-entrypoint-initdb.d/fixture/accounts.json" --jsonArray; +mongoimport --uri="mongodb://$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@localhost:27017/auth?authSource=admin&ssl=false" --file="/docker-entrypoint-initdb.d/fixture/registration_access_token.json" --jsonArray; +mongo --eval "db.auth('$MONGO_INITDB_ROOT_USERNAME', '$MONGO_INITDB_ROOT_PASSWORD'); db = db.getSiblingDB('$MONGO_INITDB_DATABASE'); db.createUser({ user: '$MONGODB_USER', pwd: '$MONGODB_PASSWORD', roles: [{ role: 'readWrite', db: '$MONGODB_NAME' }] });"; diff --git a/tsconfig.base.json b/tsconfig.base.json index c81f08a7e..ec0d614bf 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -17,11 +17,14 @@ "skipLibCheck": true, "skipDefaultLibCheck": true, "baseUrl": ".", - "isolatedModules": true, + "isolatedModules": false, "strict": true, "paths": { - "@thxnetwork/campaign*": ["apps/campaign/src*"], - "@thxnetwork/common*": ["libs/common/src*"] + "@thxnetwork/api*": ["apps/api/src/app*"], + "@thxnetwork/auth*": ["apps/auth/src/app*"], + "@thxnetwork/app*": ["apps/app/src*"], + "@thxnetwork/common*": ["libs/common/src/lib*"], + "@thxnetwork/sdk*": ["libs/sdk/src/lib*"] } }, "exclude": ["node_modules", "tmp"] diff --git a/yarn.lock b/yarn.lock index 29d70ad76..d7adeefbb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,15 +7,20 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@achrinza/node-ipc@9.2.2", "@achrinza/node-ipc@9.2.5": - version "9.2.5" - resolved "https://registry.yarnpkg.com/@achrinza/node-ipc/-/node-ipc-9.2.5.tgz#29788e608ff41121f0543491da723b243266ac28" - integrity sha512-kBX7Ay911iXZ3VZ1pYltj3Rfu7Ow9H7sK4H4RSfWIfWR2JKNB40K808wppoRIEzE2j2hXLU+r6TJgCAliCGhyQ== +"@achrinza/node-ipc@9.2.2", "@achrinza/node-ipc@9.2.7": + version "9.2.7" + resolved "https://registry.yarnpkg.com/@achrinza/node-ipc/-/node-ipc-9.2.7.tgz#cc418f9218d24d9b87f32207e5d6e71c64e241f8" + integrity sha512-/EvNkqB4HNxPWCZASmgrjqG8gIdPOolD67LGASvGMp/FY5ne0rbvpYg5o9x8RmgjAl8KdmNQ4YlV1et9DYiW8g== dependencies: "@node-ipc/js-queue" "2.0.3" event-pubsub "4.3.0" js-message "1.0.7" +"@adobe/css-tools@^4.0.1": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" + integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== + "@adraffy/ens-normalize@1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" @@ -42,6 +47,768 @@ resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.7.7.tgz#26ea493a831b4f3a85475e7157be02fb4eab51fb" integrity sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg== +"@aws-crypto/crc32@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa" + integrity sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA== + dependencies: + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + tslib "^1.11.1" + +"@aws-crypto/crc32c@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz#016c92da559ef638a84a245eecb75c3e97cb664f" + integrity sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w== + dependencies: + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + tslib "^1.11.1" + +"@aws-crypto/ie11-detection@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz#640ae66b4ec3395cee6a8e94ebcd9f80c24cd688" + integrity sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/sha1-browser@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz#f9083c00782b24714f528b1a1fef2174002266a3" + integrity sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw== + dependencies: + "@aws-crypto/ie11-detection" "^3.0.0" + "@aws-crypto/supports-web-crypto" "^3.0.0" + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz#05f160138ab893f1c6ba5be57cfd108f05827766" + integrity sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ== + dependencies: + "@aws-crypto/ie11-detection" "^3.0.0" + "@aws-crypto/sha256-js" "^3.0.0" + "@aws-crypto/supports-web-crypto" "^3.0.0" + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz#02acd1a1fda92896fc5a28ec7c6e164644ea32fc" + integrity sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g== + dependencies: + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@3.0.0", "@aws-crypto/sha256-js@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz#f06b84d550d25521e60d2a0e2a90139341e007c2" + integrity sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ== + dependencies: + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz#5d1bf825afa8072af2717c3e455f35cda0103ec2" + integrity sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/util@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-1.2.2.tgz#b28f7897730eb6538b21c18bd4de22d0ea09003c" + integrity sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg== + dependencies: + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/util@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-3.0.0.tgz#1c7ca90c29293f0883468ad48117937f0fe5bfb0" + integrity sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/client-cognito-identity@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.576.0.tgz#bb4f71cabb9d1c12e618bfe1424855c9a678ed8b" + integrity sha512-SgfR1LLZWT1NrNOB968OKC8RAbaQUFG4V1eDjAeNjtuqC7iAlY9Ogrl824XJY4muz4ErVAga7A+Xn9QTOSSTBQ== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sso-oidc" "3.576.0" + "@aws-sdk/client-sts" "3.576.0" + "@aws-sdk/core" "3.576.0" + "@aws-sdk/credential-provider-node" "3.576.0" + "@aws-sdk/middleware-host-header" "3.575.0" + "@aws-sdk/middleware-logger" "3.575.0" + "@aws-sdk/middleware-recursion-detection" "3.575.0" + "@aws-sdk/middleware-user-agent" "3.575.0" + "@aws-sdk/region-config-resolver" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@aws-sdk/util-user-agent-browser" "3.575.0" + "@aws-sdk/util-user-agent-node" "3.575.0" + "@smithy/config-resolver" "^3.0.0" + "@smithy/core" "^2.0.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/hash-node" "^3.0.0" + "@smithy/invalid-dependency" "^3.0.0" + "@smithy/middleware-content-length" "^3.0.0" + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.0" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.0" + "@smithy/util-defaults-mode-node" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-retry" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.576.0.tgz#0e2f8f8fdbf1548ed2bd7d782f8fdf2e90131de1" + integrity sha512-6Xhj8x7ijLqoLYncKMUn433QKWzEezDLR3TipKv/qHThTa8oYXkymMat/MfJ/lx3jsc8wS72i+1kTwO+AFUg6w== + dependencies: + "@aws-crypto/sha1-browser" "3.0.0" + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sso-oidc" "3.576.0" + "@aws-sdk/client-sts" "3.576.0" + "@aws-sdk/core" "3.576.0" + "@aws-sdk/credential-provider-node" "3.576.0" + "@aws-sdk/middleware-bucket-endpoint" "3.575.0" + "@aws-sdk/middleware-expect-continue" "3.575.0" + "@aws-sdk/middleware-flexible-checksums" "3.575.0" + "@aws-sdk/middleware-host-header" "3.575.0" + "@aws-sdk/middleware-location-constraint" "3.575.0" + "@aws-sdk/middleware-logger" "3.575.0" + "@aws-sdk/middleware-recursion-detection" "3.575.0" + "@aws-sdk/middleware-sdk-s3" "3.575.0" + "@aws-sdk/middleware-signing" "3.575.0" + "@aws-sdk/middleware-ssec" "3.575.0" + "@aws-sdk/middleware-user-agent" "3.575.0" + "@aws-sdk/region-config-resolver" "3.575.0" + "@aws-sdk/signature-v4-multi-region" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@aws-sdk/util-user-agent-browser" "3.575.0" + "@aws-sdk/util-user-agent-node" "3.575.0" + "@aws-sdk/xml-builder" "3.575.0" + "@smithy/config-resolver" "^3.0.0" + "@smithy/core" "^2.0.0" + "@smithy/eventstream-serde-browser" "^3.0.0" + "@smithy/eventstream-serde-config-resolver" "^3.0.0" + "@smithy/eventstream-serde-node" "^3.0.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/hash-blob-browser" "^3.0.0" + "@smithy/hash-node" "^3.0.0" + "@smithy/hash-stream-node" "^3.0.0" + "@smithy/invalid-dependency" "^3.0.0" + "@smithy/md5-js" "^3.0.0" + "@smithy/middleware-content-length" "^3.0.0" + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.0" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.0" + "@smithy/util-defaults-mode-node" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + "@smithy/util-retry" "^3.0.0" + "@smithy/util-stream" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-ses@^3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-ses/-/client-ses-3.576.0.tgz#dcbc37c095e41842e31cfdfbbe93df1f7e02df65" + integrity sha512-NevkUOGvvygAhY9WAszmiokCQMhZxeasHNjt3Y0dDflgDhR16Lau3xmVsHdwLJkr0CnjcY5r5jLKpyLK4YuwaA== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sso-oidc" "3.576.0" + "@aws-sdk/client-sts" "3.576.0" + "@aws-sdk/core" "3.576.0" + "@aws-sdk/credential-provider-node" "3.576.0" + "@aws-sdk/middleware-host-header" "3.575.0" + "@aws-sdk/middleware-logger" "3.575.0" + "@aws-sdk/middleware-recursion-detection" "3.575.0" + "@aws-sdk/middleware-user-agent" "3.575.0" + "@aws-sdk/region-config-resolver" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@aws-sdk/util-user-agent-browser" "3.575.0" + "@aws-sdk/util-user-agent-node" "3.575.0" + "@smithy/config-resolver" "^3.0.0" + "@smithy/core" "^2.0.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/hash-node" "^3.0.0" + "@smithy/invalid-dependency" "^3.0.0" + "@smithy/middleware-content-length" "^3.0.0" + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.0" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.0" + "@smithy/util-defaults-mode-node" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-retry" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso-oidc@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.576.0.tgz#0747712005dc8c70ad7b1ecdb1fb4857106a0a69" + integrity sha512-6U8933O9h6iMnQDpH3OtFhS3G3FVttYZUqTpC2T0FnSSX7zgG0GnlxdQiyZh1j1aFrEB8bFw/RSmxPcMJJuSlQ== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sts" "3.576.0" + "@aws-sdk/core" "3.576.0" + "@aws-sdk/credential-provider-node" "3.576.0" + "@aws-sdk/middleware-host-header" "3.575.0" + "@aws-sdk/middleware-logger" "3.575.0" + "@aws-sdk/middleware-recursion-detection" "3.575.0" + "@aws-sdk/middleware-user-agent" "3.575.0" + "@aws-sdk/region-config-resolver" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@aws-sdk/util-user-agent-browser" "3.575.0" + "@aws-sdk/util-user-agent-node" "3.575.0" + "@smithy/config-resolver" "^3.0.0" + "@smithy/core" "^2.0.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/hash-node" "^3.0.0" + "@smithy/invalid-dependency" "^3.0.0" + "@smithy/middleware-content-length" "^3.0.0" + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.0" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.0" + "@smithy/util-defaults-mode-node" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-retry" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.576.0.tgz#10a60057a6e6fb3cae164af1f16bb20f60188746" + integrity sha512-xbKE4bf3HYvkdrvn5kkpUdcoi3mg7uDLLkSbGaj0tzW3vNSdx9qLrCMuwfV7KrhVKWwx+lnw/2LGuCR2B5y0IA== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.576.0" + "@aws-sdk/middleware-host-header" "3.575.0" + "@aws-sdk/middleware-logger" "3.575.0" + "@aws-sdk/middleware-recursion-detection" "3.575.0" + "@aws-sdk/middleware-user-agent" "3.575.0" + "@aws-sdk/region-config-resolver" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@aws-sdk/util-user-agent-browser" "3.575.0" + "@aws-sdk/util-user-agent-node" "3.575.0" + "@smithy/config-resolver" "^3.0.0" + "@smithy/core" "^2.0.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/hash-node" "^3.0.0" + "@smithy/invalid-dependency" "^3.0.0" + "@smithy/middleware-content-length" "^3.0.0" + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.0" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.0" + "@smithy/util-defaults-mode-node" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-retry" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sts@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.576.0.tgz#17aee1dad3c135730c47df8aac2adbf14fae7df6" + integrity sha512-GHqqfRcUW/nGE4lpRafNKRxi4K7+SaQjYLjQnTEioUhr+w1IT/fFb3rGZYHHnN9ZCzbnrBULRC+1XOPIQWyLsw== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sso-oidc" "3.576.0" + "@aws-sdk/core" "3.576.0" + "@aws-sdk/credential-provider-node" "3.576.0" + "@aws-sdk/middleware-host-header" "3.575.0" + "@aws-sdk/middleware-logger" "3.575.0" + "@aws-sdk/middleware-recursion-detection" "3.575.0" + "@aws-sdk/middleware-user-agent" "3.575.0" + "@aws-sdk/region-config-resolver" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@aws-sdk/util-user-agent-browser" "3.575.0" + "@aws-sdk/util-user-agent-node" "3.575.0" + "@smithy/config-resolver" "^3.0.0" + "@smithy/core" "^2.0.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/hash-node" "^3.0.0" + "@smithy/invalid-dependency" "^3.0.0" + "@smithy/middleware-content-length" "^3.0.0" + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.0" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.0" + "@smithy/util-defaults-mode-node" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-retry" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/core@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.576.0.tgz#ced16ca42b615182565c6bcf4563278b30fd43bf" + integrity sha512-KDvDlbeipSTIf+ffKtTg1m419TK7s9mZSWC8bvuZ9qx6/sjQFOXIKOVqyuli6DnfxGbvRcwoRuY99OcCH1N/0w== + dependencies: + "@smithy/core" "^2.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/signature-v4" "^3.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + fast-xml-parser "4.2.5" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-cognito-identity@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.576.0.tgz#267ffc317defa239f1afd874a9364361c8a59718" + integrity sha512-pi5gY+VhuQk8PUskxSonRS7IZk82jbhpfLBFnbFdNDUpBPSrHAfi1AukqAgbbiB/MfJTKaI/rNg3VfwyOzPmJw== + dependencies: + "@aws-sdk/client-cognito-identity" "3.576.0" + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.575.0.tgz#2f0238719b383e37265e736575e9a9823a562982" + integrity sha512-YTgpq3rvYBXzW6OTDB00cE79evQtss/lz2GlJXgqqVXD0m7i77hGA8zb44VevP/WxtDaiSW7SSjuu8VCBGsg4g== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.575.0.tgz#d410dba2ae89ea6c42bf30d319b98e410da14d1b" + integrity sha512-xQfVmYI+9KqRvhWY8fyElnpcVUBBUgi/Hoji3oU6WLrUjrX98k93He7gKDQSyHf7ykMLUAJYWwsV4AjQ2j6njA== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/fetch-http-handler" "^3.0.0" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-stream" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.576.0.tgz#faad19e47ae8c61997d435d6d8a148d8057cada1" + integrity sha512-AwH/+29SbjhxGJVYhFn6+7r0MZ7TjJClySTJzuOoyjJGPWAifTdEuFkyOw8Bs9fEvbJ0ExgFxSaa445fO56kmg== + dependencies: + "@aws-sdk/credential-provider-env" "3.575.0" + "@aws-sdk/credential-provider-process" "3.575.0" + "@aws-sdk/credential-provider-sso" "3.576.0" + "@aws-sdk/credential-provider-web-identity" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@smithy/credential-provider-imds" "^3.0.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.576.0.tgz#9bd181e9cb2c3d1434df8293e6c0f271de0854bc" + integrity sha512-Ad244g3TJnfY1QFlZ+cywD6kgGD2yj+qg47Ryt50Y42bwmNuuqSpF9n0C71opRR68Rcl7ksOxixCJomWqpcHbA== + dependencies: + "@aws-sdk/credential-provider-env" "3.575.0" + "@aws-sdk/credential-provider-http" "3.575.0" + "@aws-sdk/credential-provider-ini" "3.576.0" + "@aws-sdk/credential-provider-process" "3.575.0" + "@aws-sdk/credential-provider-sso" "3.576.0" + "@aws-sdk/credential-provider-web-identity" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@smithy/credential-provider-imds" "^3.0.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.575.0.tgz#b1b409af833ccbce18e2806991434aa30f61ceb3" + integrity sha512-2/5NJV7MZysKglqJSQ/O8OELNcwLcH3xknabL9NagtzB7RNB2p1AUXR0UlTey9sSDLL4oCmNa/+unYuglW/Ahg== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.576.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.576.0.tgz#d3c9041fdae3513717aaea92fefc3371f64a6d83" + integrity sha512-1F17issiqf+mSG7KJ+D0SfZRYBZPAmRcA5+VHDUuMLozhh8tyYMe0mwzOt9IKc7ocrJA+2Wp7l7sg3h6aanedQ== + dependencies: + "@aws-sdk/client-sso" "3.576.0" + "@aws-sdk/token-providers" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.575.0.tgz#524ff9f944986c99486be5fa374db4d5895f63ff" + integrity sha512-QcvVH7wpvpFRXGAGgCBfQeiF/ptD0NJ+Hrc8dDYfPGhFeZ0EoVQBYNphLi25xe7JZ+XbaqCKrURHZtr4fAEOJw== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/credential-providers@^3.186.0": + version "3.576.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-providers/-/credential-providers-3.576.0.tgz#ecd1361cd2e846bf9b536079d7114c1e0ff82183" + integrity sha512-OoYyhSpxshmijD4aG/wDJIciFTh1DoNKyVyLaMGaJkE9nblArRCO+z0DEg9Yqlo8tLG0HLiTAJbyLxdQryKV5Q== + dependencies: + "@aws-sdk/client-cognito-identity" "3.576.0" + "@aws-sdk/client-sso" "3.576.0" + "@aws-sdk/client-sts" "3.576.0" + "@aws-sdk/credential-provider-cognito-identity" "3.576.0" + "@aws-sdk/credential-provider-env" "3.575.0" + "@aws-sdk/credential-provider-http" "3.575.0" + "@aws-sdk/credential-provider-ini" "3.576.0" + "@aws-sdk/credential-provider-node" "3.576.0" + "@aws-sdk/credential-provider-process" "3.575.0" + "@aws-sdk/credential-provider-sso" "3.576.0" + "@aws-sdk/credential-provider-web-identity" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@smithy/credential-provider-imds" "^3.0.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-bucket-endpoint@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.575.0.tgz#258c00c672179f6c038f532f9bc1ff51caba5eb0" + integrity sha512-ytsp7xcmbpkVk4TLoi91YyXQh/vwSIGdJ2Awo/pi6ac5Fqe6OntPijh5GHSVj5ZrxW4haPWb6HdBmKMo4liGEw== + dependencies: + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-arn-parser" "3.568.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-config-provider" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-expect-continue@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.575.0.tgz#d54c8b309b87347be9f3a3b566bafa0fbf8cd7bf" + integrity sha512-8Nq4UtEi63MJPoYBACW5YoMKQdbrkLNGIdTyrolNRNwVS+6nQqDMvBplakCzQ1nL1rHOEEsKKc8e2BlG9SkR5A== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-flexible-checksums@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.575.0.tgz#0b06111c0cc82c279e6682acd36f96e3df56a1d5" + integrity sha512-UbyqN39v6s+olyuVKwX778w6J2ZuYpxb1j+KdhFtZwpMSLd/UIQ0+A71U2vB6TrC52OEW0jIXEEBv6PcMBz9nw== + dependencies: + "@aws-crypto/crc32" "3.0.0" + "@aws-crypto/crc32c" "3.0.0" + "@aws-sdk/types" "3.575.0" + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-host-header@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.575.0.tgz#5bf24080d6a6c466cdeaff49879c77559f74a2fd" + integrity sha512-V2WoLBiXNCc4rIWZt6FUcP4TN0Vk02A9PPCBWkTfyOooiqfq+WZmZjRRBpwl1+5UsvARslrKWF0VzheMRXPJLQ== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-location-constraint@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.575.0.tgz#1816fcda54bfefa4b607a0ac93273e12b2027621" + integrity sha512-MtQsLsEjSSSfm0OlQqg9PEzS1nxJDdApGoeCYLTbCzIp6hChdLZCCsDXwGg9S++24rjQsUglMhXh4WGXQ9FDnw== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.575.0.tgz#981c939cb3c10c1e3ecfa458c64f7be8c5a71307" + integrity sha512-7DEKx9Z11Maaye7FfhYtC8rjbM/PcFcMO2N4QEAfypcgWCj+w4gseE2OGdfAH9OFDoFc6YvLp53v16vbPjzQSg== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-recursion-detection@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.575.0.tgz#924a4b7ca864600a202d82621bed3ddfd7819e06" + integrity sha512-ri89ldRFos6KZDGaknWPS2XPO9qr+gZ7+mPaoU8YkSM1W4uKqtnUSONyc+O3CFGJrqReuGHhRq0l2Sld0bjwOw== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-sdk-s3@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.575.0.tgz#b2be802e4522ca4d7b0466b1a9b174f802afc48f" + integrity sha512-8cBG8/tap4F6+UigTpKu8D2bvsLgqRTmn1K86qo3LqRX0Wc5X8TVjdKA2PmG0onOOr7rqTLcP9Q02LCh3usU6Q== + dependencies: + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-arn-parser" "3.568.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/signature-v4" "^3.0.0" + "@smithy/smithy-client" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-config-provider" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-signing@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.575.0.tgz#b19d2d6816770ed8645e12662d147777b271907e" + integrity sha512-frpGG7i3YngWwrYIeDq8/nbat3Gfl803qasaS112rmlPU0ezmYS1SPxpXjpIKxUUYofbzaFtRBAOHU1u7GnWew== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/signature-v4" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-ssec@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.575.0.tgz#799650e7860a7443e95e0e72313e41b77e99ce1c" + integrity sha512-rEFt2w3DdlmPsHRvVXOW6rNDIPE7UaEZ5a4LAkn78XilQYuQdhm5wtw5Ao0pJpDSVYNCZDVZaAvdHKQ1dnfwCA== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.575.0.tgz#1969f8b8972ee0c02753584575dde3e3b0b204b8" + integrity sha512-fWlr4RfrUNS2R3PgP+WsoMYORAgv/47Lp0J0fb3dXO1YvdczNWddRbFSUX2MQxM/y9XFfQPLpLgzluhoL3Cjeg== + dependencies: + "@aws-sdk/types" "3.575.0" + "@aws-sdk/util-endpoints" "3.575.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/region-config-resolver@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.575.0.tgz#8be6a9411ec8b5da3f3d0cac44beaa5b9eb6341f" + integrity sha512-sBJKwTWKCWu9y8FzXIijYGwkKr3tDkPXM7BylToe6W+tGkp4OirV4iXrWA9zReNwTTepoxHufofqjGK9BtcI8g== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/signature-v4-multi-region@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.575.0.tgz#73a639120a9bb36a067fdbaa094a54c9bcff7093" + integrity sha512-QMwuLuNwnEQ51RCZX8H/lXnOJgBcJJOCgClB9usW/XujNJVq8GnpZ5E7TsQLN88G6fifmcjQWonLKummuh/zVA== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.575.0" + "@aws-sdk/types" "3.575.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/signature-v4" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.575.0.tgz#0d5ded8434b49cafd7303a139d09c97155138d3b" + integrity sha512-EPNDPQoQkjKqn4D2t70qVzbfdtlaAy9KBdG58qD1yNWVxq8Rh/lXdwmB+aE2PSahtyfVikZdCRoZiFzxDh5IUA== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/types@3.575.0", "@aws-sdk/types@^3.222.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.575.0.tgz#ed8f33e15c7ea22b5244018330475983d0558556" + integrity sha512-XrnolQGs0wXxdgNudirR14OgNOarH7WUif38+2Pd4onZH+L7XoILem0EgA1tRpgFpw2pFHlZCNaAHDNSBEal7g== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/types@^3.1.0": + version "3.577.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.577.0.tgz#7700784d368ce386745f8c340d9d68cea4716f90" + integrity sha512-FT2JZES3wBKN/alfmhlo+3ZOq/XJ0C7QOZcDNrpKjB0kqYoKjhVKZ/Hx6ArR0czkKfHzBBEs6y40ebIHx2nSmA== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/util-arn-parser@3.568.0": + version "3.568.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz#6a19a8c6bbaa520b6be1c278b2b8c17875b91527" + integrity sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-endpoints@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.575.0.tgz#370ad9b1ce7df227d44447ab2135c5e110aa72d2" + integrity sha512-wC5x+V6w3kRlR6X6XVINsAPDYG+Tzs3Wthlw+YLtjuPODUNZIQAqsABHahxnekFyAvse+1929Hwo+CaL+BHZGA== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/types" "^3.0.0" + "@smithy/util-endpoints" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.568.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz#2acc4b2236af0d7494f7e517401ba6b3c4af11ff" + integrity sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-browser@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.575.0.tgz#3054cc42e0b386a34f7b0d4e8e9609e016c72eed" + integrity sha512-iADonXyaXgwvC4T0qRuDWCdKInz82GX2cyezq/oqVlL8bPY7HD8jwZZruuJdq5tkaJi1EhbO4+f1ksZqOiZKvQ== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/types" "^3.0.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.575.0.tgz#49a6ecc5f56297e1477de90f30f102c470d3dc78" + integrity sha512-kwzvBfA0LoILDOFS6BV8uOkksBHrYulP6kNXegB5eZnDSNia5DbBsXqxQ/HknNF5a429SWQw2aaQJEgQvZB1VA== + dependencies: + "@aws-sdk/types" "3.575.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/util-utf8-browser@^3.0.0": + version "3.259.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" + integrity sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/xml-builder@3.575.0": + version "3.575.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.575.0.tgz#233b2aae422dd789a078073032da1bc60317aa1d" + integrity sha512-cWgAwmbFYNCFzPwxL705+lWps0F3ZvOckufd2KKoEZUmtpVw9/txUXNrPySUXSmRTSRhoatIMABNfStWR043bQ== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.8.3": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" @@ -50,11 +817,24 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" +"@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== +"@babel/compat-data@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== + "@babel/core@7.18.5": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" @@ -97,6 +877,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.21.3": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.5.tgz#15ab5b98e101972d171aeef92ac70d8d6718f06a" + integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.24.5" + "@babel/helpers" "^7.24.5" + "@babel/parser" "^7.24.5" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.5" + "@babel/types" "^7.24.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/core@^7.23.2", "@babel/core@^7.23.9": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b" @@ -128,6 +929,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3" + integrity sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA== + dependencies: + "@babel/types" "^7.24.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -183,6 +994,21 @@ "@babel/helper-split-export-declaration" "^7.22.6" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.24.1", "@babel/helper-create-class-features-plugin@^7.24.4", "@babel/helper-create-class-features-plugin@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz#7d19da92c7e0cd8d11c09af2ce1b8e7512a6e723" + integrity sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.24.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.24.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.24.5" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" @@ -203,6 +1029,17 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" +"@babel/helper-define-polyfill-provider@^0.6.1", "@babel/helper-define-polyfill-provider@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" + integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" @@ -230,6 +1067,13 @@ dependencies: "@babel/types" "^7.23.0" +"@babel/helper-member-expression-to-functions@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz#5981e131d5c7003c7d1fa1ad49e86c9b097ec475" + integrity sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA== + dependencies: + "@babel/types" "^7.24.5" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.8.3": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" @@ -237,6 +1081,13 @@ dependencies: "@babel/types" "^7.22.15" +"@babel/helper-module-imports@^7.24.1", "@babel/helper-module-imports@^7.24.3": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + "@babel/helper-module-transforms@^7.18.0", "@babel/helper-module-transforms@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" @@ -248,6 +1099,17 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.20" +"@babel/helper-module-transforms@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz#ea6c5e33f7b262a0ae762fd5986355c45f54a545" + integrity sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.24.3" + "@babel/helper-simple-access" "^7.24.5" + "@babel/helper-split-export-declaration" "^7.24.5" + "@babel/helper-validator-identifier" "^7.24.5" + "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" @@ -265,6 +1127,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== +"@babel/helper-plugin-utils@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a" + integrity sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ== + "@babel/helper-remap-async-to-generator@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" @@ -283,6 +1150,15 @@ "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" +"@babel/helper-replace-supers@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz#7085bd19d4a0b7ed8f405c1ed73ccb70f323abc1" + integrity sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-simple-access@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" @@ -290,6 +1166,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-simple-access@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz#50da5b72f58c16b07fbd992810be6049478e85ba" + integrity sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ== + dependencies: + "@babel/types" "^7.24.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" @@ -304,16 +1187,33 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-split-export-declaration@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6" + integrity sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q== + dependencies: + "@babel/types" "^7.24.5" + "@babel/helper-string-parser@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== +"@babel/helper-string-parser@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== +"@babel/helper-validator-identifier@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" + integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== + "@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" @@ -346,6 +1246,15 @@ "@babel/traverse" "^7.24.0" "@babel/types" "^7.24.0" +"@babel/helpers@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.5.tgz#fedeb87eeafa62b621160402181ad8585a22a40a" + integrity sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.5" + "@babel/types" "^7.24.5" + "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" @@ -355,6 +1264,16 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.24.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e" + integrity sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.5", "@babel/parser@^7.20.7", "@babel/parser@^7.23.6", "@babel/parser@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" @@ -365,6 +1284,19 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac" integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg== +"@babel/parser@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" + integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz#4c3685eb9cd790bcad2843900fe0250c91ccf895" + integrity sha512-LdXRi1wEMTrHVR4Zc9F8OewC3vdm5h4QB6L71zy6StmYeqGi1b3ttIO8UC+BfZKcH9jdr4aI249rBkm+3+YvHw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" @@ -372,6 +1304,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz#b645d9ba8c2bc5b7af50f0fe949f9edbeb07c8cf" + integrity sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" @@ -381,6 +1320,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-optional-chaining" "^7.23.3" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz#da8261f2697f0f41b0855b91d3a20a1fbfd271d3" + integrity sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.24.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7": version "7.23.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b" @@ -389,6 +1337,14 @@ "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz#1181d9685984c91d657b8ddf14f0487a6bab2988" + integrity sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-proposal-class-properties@^7.8.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" @@ -483,6 +1439,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-syntax-import-assertions@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz#db3aad724153a00eaac115a3fb898de544e34971" + integrity sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-import-attributes@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" @@ -490,6 +1453,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-syntax-import-attributes@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz#c66b966c63b714c4eec508fcf5763b1f2d381093" + integrity sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -511,6 +1481,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-syntax-jsx@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" + integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -574,6 +1551,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-syntax-typescript@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" + integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" @@ -589,6 +1573,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-arrow-functions@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz#2bf263617060c9cc45bcdbf492b8cc805082bf27" + integrity sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-async-generator-functions@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz#9adaeb66fc9634a586c5df139c6240d41ed801ce" @@ -599,6 +1590,16 @@ "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-syntax-async-generators" "^7.8.4" +"@babel/plugin-transform-async-generator-functions@^7.24.3": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz#8fa7ae481b100768cc9842c8617808c5352b8b89" + integrity sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-transform-async-to-generator@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" @@ -608,6 +1609,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-remap-async-to-generator" "^7.22.20" +"@babel/plugin-transform-async-to-generator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz#0e220703b89f2216800ce7b1c53cb0cf521c37f4" + integrity sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw== + dependencies: + "@babel/helper-module-imports" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/plugin-transform-block-scoped-functions@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" @@ -615,6 +1625,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-block-scoped-functions@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz#1c94799e20fcd5c4d4589523bbc57b7692979380" + integrity sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-block-scoping@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" @@ -622,6 +1639,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-block-scoping@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.5.tgz#89574191397f85661d6f748d4b89ee4d9ee69a2a" + integrity sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-transform-class-properties@^7.22.5", "@babel/plugin-transform-class-properties@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" @@ -630,6 +1654,14 @@ "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-class-properties@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz#bcbf1aef6ba6085cfddec9fc8d58871cf011fc29" + integrity sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-class-static-block@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz#2a202c8787a8964dd11dfcedf994d36bfc844ab5" @@ -639,6 +1671,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" +"@babel/plugin-transform-class-static-block@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz#1a4653c0cf8ac46441ec406dece6e9bc590356a4" + integrity sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.4" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-transform-classes@^7.23.8": version "7.23.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" @@ -653,6 +1694,20 @@ "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.5.tgz#05e04a09df49a46348299a0e24bfd7e901129339" + integrity sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/helper-replace-supers" "^7.24.1" + "@babel/helper-split-export-declaration" "^7.24.5" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" @@ -661,6 +1716,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/template" "^7.22.15" +"@babel/plugin-transform-computed-properties@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz#bc7e787f8e021eccfb677af5f13c29a9934ed8a7" + integrity sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/template" "^7.24.0" + "@babel/plugin-transform-destructuring@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" @@ -668,6 +1731,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-destructuring@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.5.tgz#80843ee6a520f7362686d1a97a7b53544ede453c" + integrity sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-transform-dotall-regex@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" @@ -676,13 +1746,28 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-duplicate-keys@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" +"@babel/plugin-transform-dotall-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz#d56913d2f12795cc9930801b84c6f8c47513ac13" + integrity sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-duplicate-keys@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-duplicate-keys@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz#5347a797fe82b8d09749d10e9f5b83665adbca88" + integrity sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-dynamic-import@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz#c7629e7254011ac3630d47d7f34ddd40ca535143" @@ -691,6 +1776,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" +"@babel/plugin-transform-dynamic-import@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz#2a5a49959201970dd09a5fca856cb651e44439dd" + integrity sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" @@ -699,6 +1792,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-exponentiation-operator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz#6650ebeb5bd5c012d5f5f90a26613a08162e8ba4" + integrity sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-export-namespace-from@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191" @@ -707,6 +1808,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-transform-export-namespace-from@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz#f033541fc036e3efb2dcb58eedafd4f6b8078acd" + integrity sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-transform-for-of@^7.23.6": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" @@ -715,6 +1824,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" +"@babel/plugin-transform-for-of@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz#67448446b67ab6c091360ce3717e7d3a59e202fd" + integrity sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-function-name@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" @@ -724,6 +1841,15 @@ "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-function-name@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz#8cba6f7730626cc4dfe4ca2fa516215a0592b361" + integrity sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA== + dependencies: + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-json-strings@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz#a871d9b6bd171976efad2e43e694c961ffa3714d" @@ -732,6 +1858,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" +"@babel/plugin-transform-json-strings@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz#08e6369b62ab3e8a7b61089151b161180c8299f7" + integrity sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-transform-literals@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" @@ -739,6 +1873,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz#0a1982297af83e6b3c94972686067df588c5c096" + integrity sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-logical-assignment-operators@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz#e599f82c51d55fac725f62ce55d3a0886279ecb5" @@ -747,6 +1888,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" +"@babel/plugin-transform-logical-assignment-operators@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz#719d8aded1aa94b8fb34e3a785ae8518e24cfa40" + integrity sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" @@ -754,6 +1903,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-member-expression-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz#896d23601c92f437af8b01371ad34beb75df4489" + integrity sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-modules-amd@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" @@ -762,6 +1918,14 @@ "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-modules-amd@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz#b6d829ed15258536977e9c7cc6437814871ffa39" + integrity sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-modules-commonjs@^7.2.0", "@babel/plugin-transform-modules-commonjs@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" @@ -771,6 +1935,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" +"@babel/plugin-transform-modules-commonjs@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz#e71ba1d0d69e049a22bf90b3867e263823d3f1b9" + integrity sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-modules-systemjs@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz#105d3ed46e4a21d257f83a2f9e2ee4203ceda6be" @@ -781,6 +1954,16 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.20" +"@babel/plugin-transform-modules-systemjs@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz#2b9625a3d4e445babac9788daec39094e6b11e3e" + integrity sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/plugin-transform-modules-umd@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" @@ -789,6 +1972,14 @@ "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-modules-umd@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz#69220c66653a19cf2c0872b9c762b9a48b8bebef" + integrity sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" @@ -804,6 +1995,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-new-target@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz#29c59988fa3d0157de1c871a28cd83096363cc34" + integrity sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-nullish-coalescing-operator@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz#45556aad123fc6e52189ea749e33ce090637346e" @@ -812,6 +2010,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz#0cd494bb97cb07d428bd651632cb9d4140513988" + integrity sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-transform-numeric-separator@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz#03d08e3691e405804ecdd19dd278a40cca531f29" @@ -820,6 +2026,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-transform-numeric-separator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz#5bc019ce5b3435c1cadf37215e55e433d674d4e8" + integrity sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-transform-object-rest-spread@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83" @@ -842,6 +2056,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.23.3" +"@babel/plugin-transform-object-rest-spread@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.5.tgz#f91bbcb092ff957c54b4091c86bda8372f0b10ef" + integrity sha512-7EauQHszLGM3ay7a161tTQH7fj+3vVM/gThlz5HpFtnygTxjrlvoeq7MPVA1Vy9Q555OB8SnAOsMkLShNkkrHA== + dependencies: + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.24.5" + "@babel/plugin-transform-object-super@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" @@ -850,6 +2074,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.20" +"@babel/plugin-transform-object-super@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz#e71d6ab13483cca89ed95a474f542bbfc20a0520" + integrity sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-replace-supers" "^7.24.1" + "@babel/plugin-transform-optional-catch-binding@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz#318066de6dacce7d92fa244ae475aa8d91778017" @@ -858,6 +2090,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" +"@babel/plugin-transform-optional-catch-binding@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz#92a3d0efe847ba722f1a4508669b23134669e2da" + integrity sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz#6acf61203bdfc4de9d4e52e64490aeb3e52bd017" @@ -867,6 +2107,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +"@babel/plugin-transform-optional-chaining@^7.24.1", "@babel/plugin-transform-optional-chaining@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.5.tgz#a6334bebd7f9dd3df37447880d0bd64b778e600f" + integrity sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-transform-parameters@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" @@ -874,6 +2123,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-parameters@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.5.tgz#5c3b23f3a6b8fed090f9b98f2926896d3153cc62" + integrity sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-transform-private-methods@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" @@ -882,6 +2138,14 @@ "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-private-methods@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz#a0faa1ae87eff077e1e47a5ec81c3aef383dc15a" + integrity sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-private-property-in-object@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5" @@ -892,6 +2156,16 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" +"@babel/plugin-transform-private-property-in-object@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.5.tgz#f5d1fcad36e30c960134cb479f1ca98a5b06eda5" + integrity sha512-JM4MHZqnWR04jPMujQDTBVRnqxpLLpx2tkn7iPn+Hmsc0Gnb79yvRWOkvqFOx3Z7P7VxiRIR22c4eGSNj87OBQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.24.5" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-transform-property-literals@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" @@ -899,6 +2173,53 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-property-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz#d6a9aeab96f03749f4eebeb0b6ea8e90ec958825" + integrity sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-react-constant-elements@^7.21.3": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz#d493a0918b9fdad7540f5afd9b5eb5c52500d18d" + integrity sha512-QXp1U9x0R7tkiGB0FOk8o74jhnap0FlZ5gNkRIWdG3eP+SvMFg118e1zaWewDzgABb106QSKpVsD3Wgd8t6ifA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-react-display-name@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz#554e3e1a25d181f040cf698b93fd289a03bfdcdb" + integrity sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-react-jsx-development@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.22.5" + +"@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" + +"@babel/plugin-transform-react-pure-annotations@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz#c86bce22a53956331210d268e49a0ff06e392470" + integrity sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-regenerator@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" @@ -907,6 +2228,14 @@ "@babel/helper-plugin-utils" "^7.22.5" regenerator-transform "^0.15.2" +"@babel/plugin-transform-regenerator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz#625b7545bae52363bdc1fbbdc7252b5046409c8c" + integrity sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + regenerator-transform "^0.15.2" + "@babel/plugin-transform-reserved-words@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" @@ -914,6 +2243,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-reserved-words@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz#8de729f5ecbaaf5cf83b67de13bad38a21be57c1" + integrity sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-runtime@^7.11.0": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz#2c64d0680fc8e09e1dfe8fd5c646fe72abd82004" @@ -945,6 +2281,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-shorthand-properties@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz#ba9a09144cf55d35ec6b93a32253becad8ee5b55" + integrity sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-spread@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" @@ -953,6 +2296,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" +"@babel/plugin-transform-spread@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz#a1acf9152cbf690e4da0ba10790b3ac7d2b2b391" + integrity sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-sticky-regex@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" @@ -960,6 +2311,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-sticky-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz#f03e672912c6e203ed8d6e0271d9c2113dc031b9" + integrity sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-template-literals@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" @@ -967,6 +2325,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-template-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz#15e2166873a30d8617e3e2ccadb86643d327aab7" + integrity sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-typeof-symbol@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" @@ -974,6 +2339,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-typeof-symbol@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.5.tgz#703cace5ef74155fb5eecab63cbfc39bdd25fe12" + integrity sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-transform-typescript@^7.23.3": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz#aa36a94e5da8d94339ae3a4e22d40ed287feb34c" @@ -984,6 +2356,16 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.23.3" +"@babel/plugin-transform-typescript@^7.24.1": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.5.tgz#bcba979e462120dc06a75bd34c473a04781931b8" + integrity sha512-E0VWu/hk83BIFUWnsKZ4D81KXjN5L3MobvevOHErASk9IPwKHOkTgvqzvNo1yP/ePJWqqK2SpUR5z+KQbl6NVw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.24.5" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-syntax-typescript" "^7.24.1" + "@babel/plugin-transform-unicode-escapes@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" @@ -991,6 +2373,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-unicode-escapes@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz#fb3fa16676549ac7c7449db9b342614985c2a3a4" + integrity sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-unicode-property-regex@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" @@ -999,6 +2388,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-unicode-property-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz#56704fd4d99da81e5e9f0c0c93cabd91dbc4889e" + integrity sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-unicode-regex@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" @@ -1007,6 +2404,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-unicode-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz#57c3c191d68f998ac46b708380c1ce4d13536385" + integrity sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-transform-unicode-sets-regex@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" @@ -1015,6 +2420,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-unicode-sets-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz#c1ea175b02afcffc9cf57a9c4658326625165b7f" + integrity sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/preset-env@^7.11.0": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.9.tgz#beace3b7994560ed6bf78e4ae2073dff45387669" @@ -1101,6 +2514,93 @@ core-js-compat "^3.31.0" semver "^6.3.1" +"@babel/preset-env@^7.20.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.5.tgz#6a9ac90bd5a5a9dae502af60dfc58c190551bbcd" + integrity sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ== + dependencies: + "@babel/compat-data" "^7.24.4" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.24.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.24.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.24.1" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.24.1" + "@babel/plugin-syntax-import-attributes" "^7.24.1" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.24.1" + "@babel/plugin-transform-async-generator-functions" "^7.24.3" + "@babel/plugin-transform-async-to-generator" "^7.24.1" + "@babel/plugin-transform-block-scoped-functions" "^7.24.1" + "@babel/plugin-transform-block-scoping" "^7.24.5" + "@babel/plugin-transform-class-properties" "^7.24.1" + "@babel/plugin-transform-class-static-block" "^7.24.4" + "@babel/plugin-transform-classes" "^7.24.5" + "@babel/plugin-transform-computed-properties" "^7.24.1" + "@babel/plugin-transform-destructuring" "^7.24.5" + "@babel/plugin-transform-dotall-regex" "^7.24.1" + "@babel/plugin-transform-duplicate-keys" "^7.24.1" + "@babel/plugin-transform-dynamic-import" "^7.24.1" + "@babel/plugin-transform-exponentiation-operator" "^7.24.1" + "@babel/plugin-transform-export-namespace-from" "^7.24.1" + "@babel/plugin-transform-for-of" "^7.24.1" + "@babel/plugin-transform-function-name" "^7.24.1" + "@babel/plugin-transform-json-strings" "^7.24.1" + "@babel/plugin-transform-literals" "^7.24.1" + "@babel/plugin-transform-logical-assignment-operators" "^7.24.1" + "@babel/plugin-transform-member-expression-literals" "^7.24.1" + "@babel/plugin-transform-modules-amd" "^7.24.1" + "@babel/plugin-transform-modules-commonjs" "^7.24.1" + "@babel/plugin-transform-modules-systemjs" "^7.24.1" + "@babel/plugin-transform-modules-umd" "^7.24.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.24.1" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.1" + "@babel/plugin-transform-numeric-separator" "^7.24.1" + "@babel/plugin-transform-object-rest-spread" "^7.24.5" + "@babel/plugin-transform-object-super" "^7.24.1" + "@babel/plugin-transform-optional-catch-binding" "^7.24.1" + "@babel/plugin-transform-optional-chaining" "^7.24.5" + "@babel/plugin-transform-parameters" "^7.24.5" + "@babel/plugin-transform-private-methods" "^7.24.1" + "@babel/plugin-transform-private-property-in-object" "^7.24.5" + "@babel/plugin-transform-property-literals" "^7.24.1" + "@babel/plugin-transform-regenerator" "^7.24.1" + "@babel/plugin-transform-reserved-words" "^7.24.1" + "@babel/plugin-transform-shorthand-properties" "^7.24.1" + "@babel/plugin-transform-spread" "^7.24.1" + "@babel/plugin-transform-sticky-regex" "^7.24.1" + "@babel/plugin-transform-template-literals" "^7.24.1" + "@babel/plugin-transform-typeof-symbol" "^7.24.5" + "@babel/plugin-transform-unicode-escapes" "^7.24.1" + "@babel/plugin-transform-unicode-property-regex" "^7.24.1" + "@babel/plugin-transform-unicode-regex" "^7.24.1" + "@babel/plugin-transform-unicode-sets-regex" "^7.24.1" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.10.4" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.31.0" + semver "^6.3.1" + "@babel/preset-env@^7.23.2": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.0.tgz#11536a7f4b977294f0bdfad780f01a8ac8e183fc" @@ -1196,6 +2696,29 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-react@^7.18.6": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.24.1.tgz#2450c2ac5cc498ef6101a6ca5474de251e33aa95" + integrity sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-transform-react-display-name" "^7.24.1" + "@babel/plugin-transform-react-jsx" "^7.23.4" + "@babel/plugin-transform-react-jsx-development" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations" "^7.24.1" + +"@babel/preset-typescript@^7.21.0": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz#89bdf13a3149a17b3b2a2c9c62547f06db8845ec" + integrity sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-syntax-jsx" "^7.24.1" + "@babel/plugin-transform-modules-commonjs" "^7.24.1" + "@babel/plugin-transform-typescript" "^7.24.1" + "@babel/preset-typescript@^7.22.5": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913" @@ -1276,6 +2799,22 @@ debug "^4.3.1" globals "^11.1.0" +"@babel/traverse@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8" + integrity sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA== + dependencies: + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.24.5" + "@babel/parser" "^7.24.5" + "@babel/types" "^7.24.5" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.4", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" @@ -1285,6 +2824,15 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.21.3", "@babel/types@^7.23.4", "@babel/types@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" + integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ== + dependencies: + "@babel/helper-string-parser" "^7.24.1" + "@babel/helper-validator-identifier" "^7.24.5" + to-fast-properties "^2.0.0" + "@babel/types@^7.24.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" @@ -1336,6 +2884,42 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@chainsafe/as-sha256@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" + integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg== + +"@chainsafe/persistent-merkle-tree@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz#4c9ee80cc57cd3be7208d98c40014ad38f36f7ff" + integrity sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + +"@chainsafe/persistent-merkle-tree@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz#2b4a62c9489a5739dedd197250d8d2f5427e9f63" + integrity sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + +"@chainsafe/ssz@^0.10.0": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.10.2.tgz#c782929e1bb25fec66ba72e75934b31fd087579e" + integrity sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + "@chainsafe/persistent-merkle-tree" "^0.5.0" + +"@chainsafe/ssz@^0.9.2": + version "0.9.4" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.9.4.tgz#696a8db46d6975b600f8309ad3a12f7c0e310497" + integrity sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + "@chainsafe/persistent-merkle-tree" "^0.4.2" + case "^1.6.3" + "@chaitanyapotti/register-service-worker@^1.7.3": version "1.7.3" resolved "https://registry.yarnpkg.com/@chaitanyapotti/register-service-worker/-/register-service-worker-1.7.3.tgz#0e670b8a4de1c319a147700d7ebb4f8a2b2e1b84" @@ -1361,6 +2945,19 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@contrast/fn-inspect@^3.3.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@contrast/fn-inspect/-/fn-inspect-3.4.0.tgz#273b0802438c842b84fa39b16dc3a3979a96c481" + integrity sha512-Jw6dMFEIt/FXF1ihJri2GFNayeEKQ6r+WRjjWl7MdgMup2D4vCPu99ZV8eHSMqNNkj3BEzUNC91ZaJVB1XJmfg== + dependencies: + nan "^2.17.0" + node-gyp-build "^4.6.0" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1400,6 +2997,80 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@discordjs/builders@^1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.1.tgz#5bca6e50a012492ecc03480ced53cbc7a1aac054" + integrity sha512-GkF+HM01FHy+NSoTaUPR8z44otfQgJ1AIsRxclYGUZDyUbdZEFyD/5QVv2Y1Flx6M+B0bQLzg2M9CJv5lGTqpA== + dependencies: + "@discordjs/formatters" "^0.4.0" + "@discordjs/util" "^1.1.0" + "@sapphire/shapeshift" "^3.9.7" + discord-api-types "0.37.83" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.4" + tslib "^2.6.2" + +"@discordjs/collection@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.3.tgz#5a1250159ebfff9efa4f963cfa7e97f1b291be18" + integrity sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ== + +"@discordjs/collection@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.1.0.tgz#f327d944ab2dcf9a1f674470a481f78a120a5e3b" + integrity sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw== + +"@discordjs/formatters@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.4.0.tgz#066a2c2163b26ac066e6f621f17445be9690c6a9" + integrity sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ== + dependencies: + discord-api-types "0.37.83" + +"@discordjs/rest@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.3.0.tgz#06d37c7fb54a9be61134b5bbb201abd760343472" + integrity sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg== + dependencies: + "@discordjs/collection" "^2.1.0" + "@discordjs/util" "^1.1.0" + "@sapphire/async-queue" "^1.5.2" + "@sapphire/snowflake" "^3.5.3" + "@vladfrangu/async_event_emitter" "^2.2.4" + discord-api-types "0.37.83" + magic-bytes.js "^1.10.0" + tslib "^2.6.2" + undici "6.13.0" + +"@discordjs/util@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.1.0.tgz#dcffd2b61aab8eadd66bea67811bc34fc769bb2a" + integrity sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg== + +"@discordjs/ws@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.1.0.tgz#1fe3ab9979c77b44c9af6544b8e96c60940ad49a" + integrity sha512-O97DIeSvfNTn5wz5vaER6ciyUsr7nOqSEtsLoMhhIgeFkhnxLRqSr00/Fpq2/ppLgjDGLbQCDzIK7ilGoB/M7A== + dependencies: + "@discordjs/collection" "^2.1.0" + "@discordjs/rest" "^2.3.0" + "@discordjs/util" "^1.1.0" + "@sapphire/async-queue" "^1.5.2" + "@types/ws" "^8.5.10" + "@vladfrangu/async_event_emitter" "^2.2.4" + discord-api-types "0.37.83" + tslib "^2.6.2" + ws "^8.16.0" + "@emotion/babel-plugin@^11.11.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" @@ -1629,12 +3300,12 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": version "4.10.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== -"@eslint/eslintrc@^2.1.2", "@eslint/eslintrc@^2.1.4": +"@eslint/eslintrc@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== @@ -1649,11 +3320,6 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.48.0": - version "8.48.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.48.0.tgz#642633964e217905436033a2bd08bf322849b7fb" - integrity sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw== - "@eslint/js@8.57.0": version "8.57.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" @@ -1707,7 +3373,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.4.7", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -1746,7 +3412,7 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.4.0", "@ethersproject/address@^5.7.0": +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.4.0", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -1905,7 +3571,7 @@ dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.4.5": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.4.5", "@ethersproject/providers@^5.4.7", "@ethersproject/providers@^5.7.0", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -2004,7 +3670,7 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" -"@ethersproject/units@5.7.0": +"@ethersproject/units@5.7.0", "@ethersproject/units@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== @@ -2056,6 +3722,11 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@floating-ui/core@^1.0.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1" @@ -2085,13 +3756,79 @@ "@floating-ui/utils" "^0.2.1" vue-demi ">=0.13.0" -"@hapi/address@2.x.x": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" - integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== +"@gala-chain/api@1.1.20": + version "1.1.20" + resolved "https://registry.yarnpkg.com/@gala-chain/api/-/api-1.1.20.tgz#acace81f278a66414d8882b65b7b8be1ea8ab025" + integrity sha512-6MOfdhN8IXL6uhjwo7wwPyPH+OpF8R4qv3ZA7z+1HiozvFWDK1DQKNWW3OSVKh+/yL4Z+skV0zDDfvBjdKx3Lw== + dependencies: + bignumber.js "^9.0.2" + bn.js "^4.12.0" + class-transformer "0.5.1" + class-validator "^0.14.0" + class-validator-jsonschema "^5.0.0" + elliptic "^6.5.4" + js-sha3 "^0.9.3" + json-stringify-deterministic "^1.0.7" + openapi3-ts "^3.2.0" + reflect-metadata "^0.1.13" + tslib "^2.6.2" -"@hapi/bourne@1.x.x": - version "1.3.2" +"@gala-chain/client@^1.1.20": + version "1.1.20" + resolved "https://registry.yarnpkg.com/@gala-chain/client/-/client-1.1.20.tgz#8138e56e15715254bb7bb902b5222c177e43413c" + integrity sha512-AEp4d40iEcLz22UP2JMqucywP4BYrAHJsTZlCmEt4dRpZOAyrnwPEOSMNZx+kn+8KMaoLLxbSFuBFxuZdkGt0Q== + dependencies: + "@gala-chain/api" "1.1.20" + axios "^1.6.0" + jsonschema "^1.4.1" + process "0.11.10" + tslib "^2.6.2" + +"@gnosis.pm/safe-contracts@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.3.0.tgz#316741a7690d8751a1f701538cfc9ec80866eedc" + integrity sha512-1p+1HwGvxGUVzVkFjNzglwHrLNA67U/axP0Ct85FzzH8yhGJb4t9jDjPYocVMzLorDoWAfKicGy1akPY9jXRVw== + +"@godaddy/terminus@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@godaddy/terminus/-/terminus-4.12.1.tgz#c4fdc280a4ac9655d4734250e22299a4ad46c54c" + integrity sha512-Tm+wVu1/V37uZXcT7xOhzdpFoovQReErff8x3y82k6YyWa1gzxWBjTyrx4G2enjEqoXPnUUmJ3MOmwH+TiP6Sw== + dependencies: + stoppable "^1.1.0" + +"@grpc/grpc-js@^1.9.4": + version "1.10.7" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.10.7.tgz#1abce1a8c4c90b79dbbe57d7e4310f3b0ce72899" + integrity sha512-ZMBVjSeDAz3tFSehyO6Pd08xZT1HfIwq3opbeM4cDlBh52gmwp0wVIPcQur53NN0ac68HMZ/7SF2rGRD5KmVmg== + dependencies: + "@grpc/proto-loader" "^0.7.13" + "@js-sdsl/ordered-map" "^4.4.2" + +"@grpc/grpc-js@~1.9.0": + version "1.9.14" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.14.tgz#236378822876cbf7903f9d61a0330410e8dcc5a1" + integrity sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.13", "@grpc/proto-loader@^0.7.5", "@grpc/proto-loader@^0.7.8": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" + integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + +"@hapi/address@2.x.x": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== + +"@hapi/bourne@1.x.x": + version "1.3.2" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== @@ -2117,7 +3854,33 @@ dependencies: "@hapi/hoek" "^8.3.0" -"@humanwhocodes/config-array@^0.11.10", "@humanwhocodes/config-array@^0.11.14": +"@hokify/agenda@^6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@hokify/agenda/-/agenda-6.3.0.tgz#ecc4efd0dab43983c4b74d8d4e3c046fe00a370b" + integrity sha512-fWrKMDe/8QHJXLOdEsMogb6cb213Z82iNsnU7nFrSIMFifEXSkXNTyCZ99FV3KLf+Du1gS/M9/8uTC6FHyWRZQ== + dependencies: + cron-parser "^4" + date.js "~0.3.3" + debug "~4" + human-interval "~2" + luxon "^3" + mongodb "^4" + +"@hubspot/api-client@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@hubspot/api-client/-/api-client-11.1.0.tgz#f9c6cdc2f635a799152f8f7c762ddbb9acb24126" + integrity sha512-HUkvUJKKvKhED0RZfpjnTX+b5+RNRlFfAllT7ozLP7gEoT5fp5aT148mnBdNZTr1UPfScBdDQa42ixHjgkIlNQ== + dependencies: + "@types/node-fetch" "^2.5.7" + bottleneck "^2.19.5" + es6-promise "^4.2.4" + form-data "^2.5.0" + lodash.get "^4.4.2" + lodash.merge "^4.6.2" + node-fetch "^2.6.0" + url-parse "^1.4.3" + +"@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== @@ -2403,6 +4166,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" @@ -2424,7 +4195,7 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24": +"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -2432,11 +4203,28 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@js-sdsl/ordered-map@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" + integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== + +"@koa/cors@^3.3.0": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.4.3.tgz#d669ee6e8d6e4f0ec4a7a7b0a17e7a3ed3752ebb" + integrity sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw== + dependencies: + vary "^1.1.2" + "@kurkle/color@^0.3.0": version "0.3.2" resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f" integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" + integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== + "@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0", "@lit-labs/ssr-dom-shim@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz#353ce4a76c83fadec272ea5674ede767650762fd" @@ -2456,6 +4244,21 @@ dependencies: "@lit-labs/ssr-dom-shim" "^1.2.0" +"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@metamask/eth-json-rpc-provider@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-1.0.1.tgz#3fd5316c767847f4ca107518b611b15396a5a32c" @@ -2465,6 +4268,17 @@ "@metamask/safe-event-emitter" "^3.0.0" "@metamask/utils" "^5.0.1" +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@metamask/json-rpc-engine@^7.0.0": version "7.3.2" resolved "https://registry.yarnpkg.com/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.2.tgz#e8f0695811619eef7b7c894ba5cf782db9f1c2cb" @@ -2617,6 +4431,13 @@ semver "^7.5.4" superstruct "^1.0.3" +"@mongodb-js/saslprep@^1.1.0", "@mongodb-js/saslprep@^1.1.5": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz#d1700facfd6916c50c2c88fd6d48d363a56c702f" + integrity sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q== + dependencies: + sparse-bitfield "^3.0.3" + "@motionone/animation@^10.15.1", "@motionone/animation@^10.17.0": version "10.17.0" resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.17.0.tgz#7633c6f684b5fee2b61c405881b8c24662c68fca" @@ -2694,6 +4515,53 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@newrelic/native-metrics@^10.0.0": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@newrelic/native-metrics/-/native-metrics-10.1.1.tgz#5e19f3fbd2e36d33b9e11d4bfa892f3966ba26b9" + integrity sha512-BvdTMAqS3d94ZwJ6u70dWqZVkX8ev3dybkxRInHMbKV2DE1koQR3nzH2ut3hf1MaRQh4SF6SpUNTUznzCZZtjw== + dependencies: + nan "^2.18.0" + node-gyp "^10.1.0" + node-gyp-build "^4.8.0" + prebuildify "^6.0.0" + semver "^7.5.2" + +"@newrelic/ritm@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@newrelic/ritm/-/ritm-7.2.0.tgz#2d5dd52341e79ea921b7d1a57f43f9ca3b11defe" + integrity sha512-I4iVhm+wlTEDJXQT8EydF/U5vlR9bBHrtBGyvd/D9WCucoMtrPrCNyILQh9bZ+46E8QRE7zh6QEGyQcnc3qNMg== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +"@newrelic/security-agent@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@newrelic/security-agent/-/security-agent-1.2.0.tgz#cdfc2b8bd905c54c2b4f60d70b8f829f30712a5b" + integrity sha512-Snk++TQmqHKuxPYOH5bEU4GCr5xKYurUZWx3oiuoQUV73pw61qeEMrb/8iuGgAghwpCEC/8n+308efqCIZkiiQ== + dependencies: + axios "^1.6.8" + check-disk-space "^3.4.0" + content-type "^1.0.5" + fast-safe-stringify "^2.1.1" + find-package-json "^1.2.0" + hash.js "^1.1.7" + html-entities "^2.3.6" + is-invalid-path "^1.0.2" + js-yaml "^4.1.0" + jsonschema "^1.4.1" + lodash "^4.17.21" + log4js "^6.9.1" + pretty-bytes "^5.6.0" + request-ip "^3.3.0" + ringbufferjs "^2.0.0" + semver "^7.5.4" + sync-request "^6.1.0" + unescape "^1.0.1" + unescape-js "^1.1.4" + uuid "^9.0.1" + ws "^8.14.2" + "@noble/curves@1.2.0", "@noble/curves@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" @@ -2708,6 +4576,11 @@ dependencies: "@noble/hashes" "1.3.3" +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + "@noble/hashes@1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" @@ -2718,6 +4591,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== +"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@node-ipc/js-queue@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz#ac7fe33d766fa53e233ef8fedaf3443a01c5a4cd" @@ -2751,19 +4629,288 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nrwl/cypress@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/cypress/-/cypress-18.0.8.tgz#6f3642e48d1a0806a1228e123f59149aef4ae4a0" - integrity sha512-jI2QSALuLlU6YUz+KiMfAA9KRtlCELECBGzxrnZtJvQw9ln/levYzxgn4VL0HNUGa31zzRZLCPUQEQqymbawjQ== +"@nomicfoundation/ethereumjs-block@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz#6f89664f55febbd723195b6d0974773d29ee133d" + integrity sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + ethereum-cryptography "0.1.3" + ethers "^5.7.1" + +"@nomicfoundation/ethereumjs-blockchain@7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.1.tgz#80e0bd3535bfeb9baa29836b6f25123dab06a726" + integrity sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A== + dependencies: + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-ethash" "3.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + abstract-level "^1.0.3" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + level "^8.0.0" + lru-cache "^5.1.1" + memory-level "^1.0.0" + +"@nomicfoundation/ethereumjs-common@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.1.tgz#4702d82df35b07b5407583b54a45bf728e46a2f0" + integrity sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g== + dependencies: + "@nomicfoundation/ethereumjs-util" "9.0.1" + crc-32 "^1.2.0" + +"@nomicfoundation/ethereumjs-ethash@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.1.tgz#65ca494d53e71e8415c9a49ef48bc921c538fc41" + integrity sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w== + dependencies: + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + abstract-level "^1.0.3" + bigint-crypto-utils "^3.0.23" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-evm@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.1.tgz#f35681e203363f69ce2b3d3bf9f44d4e883ca1f1" + integrity sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ== + dependencies: + "@ethersproject/providers" "^5.7.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + mcl-wasm "^0.7.1" + rustbn.js "~0.2.0" + +"@nomicfoundation/ethereumjs-rlp@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.1.tgz#0b30c1cf77d125d390408e391c4bb5291ef43c28" + integrity sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ== + +"@nomicfoundation/ethereumjs-statemanager@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.1.tgz#8824a97938db4471911e2d2f140f79195def5935" + integrity sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + ethers "^5.7.1" + js-sdsl "^4.1.4" + +"@nomicfoundation/ethereumjs-trie@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.1.tgz#662c55f6b50659fd4b22ea9f806a7401cafb7717" + integrity sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + "@types/readable-stream" "^2.3.13" + ethereum-cryptography "0.1.3" + readable-stream "^3.6.0" + +"@nomicfoundation/ethereumjs-tx@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.1.tgz#7629dc2036b4a33c34e9f0a592b43227ef4f0c7d" + integrity sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w== + dependencies: + "@chainsafe/ssz" "^0.9.2" + "@ethersproject/providers" "^5.7.2" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-util@9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.1.tgz#530cda8bae33f8b5020a8f199ed1d0a2ce48ec89" + integrity sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA== + dependencies: + "@chainsafe/ssz" "^0.10.0" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-vm@7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.1.tgz#7d035e0993bcad10716c8b36e61dfb87fa3ca05f" + integrity sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ== + dependencies: + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-blockchain" "7.0.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-evm" "2.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-statemanager" "2.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + mcl-wasm "^0.7.1" + rustbn.js "~0.2.0" + +"@nomicfoundation/hardhat-chai-matchers@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.6.tgz#72a2e312e1504ee5dd73fe302932736432ba96bc" + integrity sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@types/chai-as-promised" "^7.1.3" + chai-as-promised "^7.1.1" + deep-eql "^4.0.1" + ordinal "^1.0.3" + +"@nomicfoundation/hardhat-network-helpers@^1.0.0": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz#c61042ceb104fdd6c10017859fdef6529c1d6585" + integrity sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ== + dependencies: + ethereumjs-util "^7.1.4" + +"@nomicfoundation/hardhat-toolbox@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.2.tgz#ec95f23b53cb4e71a1a7091380fa223aad18f156" + integrity sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg== + +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz#4c858096b1c17fe58a474fe81b46815f93645c15" + integrity sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz#6e25ccdf6e2d22389c35553b64fe6f3fdaec432c" + integrity sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA== + +"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz#0a224ea50317139caeebcdedd435c28a039d169c" + integrity sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz#dfa085d9ffab9efb2e7b383aed3f557f7687ac2b" + integrity sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz#c9e06b5d513dd3ab02a7ac069c160051675889a4" + integrity sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz#8d328d16839e52571f72f2998c81e46bf320f893" + integrity sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz#9b49d0634b5976bb5ed1604a1e1b736f390959bb" + integrity sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w== + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz#e2867af7264ebbcc3131ef837878955dd6a3676f" + integrity sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg== + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz#0685f78608dd516c8cdfb4896ed451317e559585" + integrity sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz#c9a44f7108646f083b82e851486e0f6aeb785836" + integrity sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz#f5f4d36d3f66752f59a57e7208cd856f3ddf6f2d" + integrity sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.1" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" + +"@nomiclabs/hardhat-ethers@^2.0.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz#b41053e360c31a32c2640c9a45ee981a7e603fe0" + integrity sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg== + +"@nomiclabs/hardhat-etherscan@^3.0.0": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" + integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" + debug "^4.1.1" + fs-extra "^7.0.1" + lodash "^4.17.11" + semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" + +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== + dependencies: + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" + +"@npmcli/fs@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726" + integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== + dependencies: + semver "^7.3.5" + +"@nrwl/cypress@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/cypress/-/cypress-18.3.0.tgz#885c1cb661f43b1aafe82d8aeaafc4db341aa9ea" + integrity sha512-bSzHKqjx7De+Ax0zTX5z8VHii5uLLSxzSIqh9lJET1leZkwtoGhe9MWdXYrfeJaTp+xV6DJAA11Rg1SWn2SInQ== + dependencies: + "@nx/cypress" "18.3.0" + +"@nrwl/devkit@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-18.3.0.tgz#2614467d5986c3ad98984bed2be00b747656e45a" + integrity sha512-JA6NJTAxxz+zZtS/jzeUMVdgXXjmWTuG8NdqJ70OxKok570afHxZSCjR32cWWmoCJRS4ASM2UpL/3292zk1wsQ== dependencies: - "@nx/cypress" "18.0.8" + "@nx/devkit" "18.3.0" -"@nrwl/devkit@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-18.0.8.tgz#9f412c9793615e18f49565a0e58a28207c6d9c21" - integrity sha512-cKtXq2I/3y/t1I+jMn8XVfhtSjGxJHKGSmxStMdRPMcUim8iaS2V3fDUdF2CGrXrtbmDtYwBC8413YY+nVh0Gw== +"@nrwl/devkit@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-19.0.4.tgz#dcfb35625ca47fd770bd1417d83e08499bb471bd" + integrity sha512-wOb7qiluWjVgmfhIGxWXAgJ61ZoL7rDYfx0mibPhbBlqm+86NHJ9CbKTfbfamS20fkzCYdhYeE6xd7sdpcZIZA== dependencies: - "@nx/devkit" "18.0.8" + "@nx/devkit" "19.0.4" "@nrwl/devkit@^14.3.6": version "14.8.9" @@ -2775,55 +4922,112 @@ ignore "^5.0.4" tslib "^2.3.0" -"@nrwl/eslint-plugin-nx@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-18.0.8.tgz#b5b2d84e15e710ea78a46887e37acda045be5e79" - integrity sha512-R7Hj8jcjrxX2Lb1fLhX0EycEx8FrzC+8VdIR6cjln+3pXPLMAQTlcWmTFCzA6272atNpc0ZudZh2dShHvhYM+w== +"@nrwl/esbuild@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/esbuild/-/esbuild-18.3.0.tgz#cb3e8264a27ff39042d613eb4664ed9506d6084e" + integrity sha512-wZNbVe+vdgTmFUzgiySWILFQweDlicUa9nOvy41Pufhx0SSeC5wcZRmuf5p1GgK5Srg36lAmklHIfcRX7OwRbw== + dependencies: + "@nx/esbuild" "18.3.0" + +"@nrwl/eslint-plugin-nx@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-18.3.0.tgz#a50cf0d5319f6355d3b6b4450f527162c148ae47" + integrity sha512-I+1dnG2xsHpD5ii2Ow58piXC95ob9rRZ6Yf0JfFed4sKxq6ntArdDeGpM4tCSNZvpRpR9kUi9UMaQA5JoLm8Jw== + dependencies: + "@nx/eslint-plugin" "18.3.0" + +"@nrwl/jest@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-18.3.0.tgz#b4e41976848077afac5bc45189eacff8e4c0946b" + integrity sha512-u1iGqhLedfmxXzJEWsAXUIgF8sQXzj8DTqLp6NUN8mJfPYCQjVOQirwl4lcNhs0gTvIgqr3wGIHo33ixyjMjFw== + dependencies: + "@nx/jest" "18.3.0" + +"@nrwl/jest@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-19.0.4.tgz#f50e0f727086ddbb9f96c4073b6011513752448c" + integrity sha512-47nASzd1lwP3/vBX2wp8xb0s+tZCSxmLf3jN+NUDNm/nxCf6/pUS5SdWJHABbioG8myrqKGaCOW5/4NCdlQD+Q== + dependencies: + "@nx/jest" "19.0.4" + +"@nrwl/js@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-18.3.0.tgz#4963d8231fd68b03932574bf54ee903d1b720d47" + integrity sha512-sLQGUkFY/9spMqe3EvkTRh9iDqIZ65HLYALaaK5RyH5z7ctXwZGgDTwvCpO9r/jEIyE1inxUNzqbYl66R7qEdQ== + dependencies: + "@nx/js" "18.3.0" + +"@nrwl/js@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-19.0.4.tgz#c9e4108266e6d166042054c582ff9f698dd058dd" + integrity sha512-BMH7ysvCXETrOXIYCfsubQUH6ToJk2gDcINQFXWa+m+D1KKQXXpOrMA/2nqRpP+JT2nQ770zERC7WEDxGzrNpA== + dependencies: + "@nx/js" "19.0.4" + +"@nrwl/node@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/node/-/node-18.3.0.tgz#f0c2ee2deeea802c305efb2449340df1637aed1e" + integrity sha512-N5PVvXJBycvKXqLRC1R5+WXniuynQgBHjyNOZzu9/R+yIrqbwuA+MjptpVHLGqCTtIgykPd2LUhmI6SHLrlZrQ== dependencies: - "@nx/eslint-plugin" "18.0.8" + "@nx/node" "18.3.0" -"@nrwl/jest@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-18.0.8.tgz#bfe99f932305c52aacd45c6b432c7ab69367e11d" - integrity sha512-kOYx295kItv0rdNOO7+a4CvTVXvi82wp3qNDmqpYGckcFQCvgTXrMKITok67vOQlN4Iip3KL/4bqBzwR0uUuzA== +"@nrwl/node@19.0.4", "@nrwl/node@^19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nrwl/node/-/node-19.0.4.tgz#ddb3d1efeefdb386911171f01fb706162a3ed5a6" + integrity sha512-9FYAS7oUDx3Mvhoa/+Cu5GwB4N19jJfVLt2QHzrzQMjNkW6DZk1N6xvyUjv41L8EbUvTYdkddZPhnXaSkzdNvQ== dependencies: - "@nx/jest" "18.0.8" + "@nx/node" "19.0.4" -"@nrwl/js@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-18.0.8.tgz#ee6006c8a15c6d5b8b37534806f50489d3a0cf4b" - integrity sha512-5VWvJD7zWFFEVfVFpKj5NQQVn9YBiNALIcHtq1A6/GEe/SrWGjTwyeK+5uVSYegTR8BXX2SpBaE53bOunWuTrQ== +"@nrwl/tao@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-18.3.0.tgz#2148e57d89dfcab0935f71cae3c48cc9a3349b09" + integrity sha512-M0m0QRiW7N+f+N+ey/gobPLYzUn932obMXDnb+6ImLsqRunFndd7YKHXUMf+y1441w7OXI5owTjE5bEKxZjOow== dependencies: - "@nx/js" "18.0.8" + nx "18.3.0" + tslib "^2.3.0" -"@nrwl/tao@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-18.0.8.tgz#78b87d179e71d10bdf87778ea3e5ae4443fec9e5" - integrity sha512-zBzdv9mGBaWtBbujbLCVzG7ZI5npUg9fnUz8VtjN6jydAQEL/Uqj5mPlFYQPPBAw2xwF8TL9ZX/rOoAWHnJtjw== +"@nrwl/tao@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-19.0.4.tgz#e8a3b86b7e157ff184aa6b79f5b427b11475954c" + integrity sha512-ZoHM5hbj0fOaWiiQoN/Wjozc6lbBCCcH7jCIX7amN6aztmcwNYk+Q3NKJE5Jh0/Js5M78VTnLRG2h4KHPzKSKg== dependencies: - nx "18.0.8" + nx "19.0.4" tslib "^2.3.0" -"@nrwl/vite@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/vite/-/vite-18.0.8.tgz#7f84737ca5282eaa049648a66d22e529616da458" - integrity sha512-b5rsC+sjNHsBb6k2B67x3yc9MnhlIpXfPhISRjRcI0XGrjNBC2Vz5PkN6xOlV+NCy/ez1Kv3uVMBiKHcUd5x7Q== +"@nrwl/vite@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/vite/-/vite-18.3.0.tgz#e6e48ef66f33fbb930d44e78f4837cbe37fa19ad" + integrity sha512-uOdSnvdIW/Rjn4tUYtU8I2vD8pFYdtI6ZJGUkB0OGjGSdGuOHPnSmOYzEt1uZKWVQe0mnJHKVd/FhMDOmSPKfA== + dependencies: + "@nx/vite" "18.3.0" + +"@nrwl/web@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/web/-/web-18.3.0.tgz#2e7e08b504a1a608b940b249dbfc08bd95453452" + integrity sha512-e3IA905VOXAm3behYIeBW6Yi9YZeNxya+RWe5kFYFR+wg/JdGNF+NrFv6IYzzY9PFo2wn0ubOrNdnCXMVz1UHA== + dependencies: + "@nx/web" "18.3.0" + +"@nrwl/webpack@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/webpack/-/webpack-18.3.0.tgz#f675e5363e5fa5d5742456c84c8fc554d30673c1" + integrity sha512-79BUpNWnDDFxd6Eoc0Q+i3WE9Fjnpjt7LIC9d52Aw6RYSv1yQLZ2D1gB9kIQo8xQrN8E5NlLZ9gwTRWYLGv+Uw== dependencies: - "@nx/vite" "18.0.8" + "@nx/webpack" "18.3.0" -"@nrwl/web@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/web/-/web-18.0.8.tgz#6f09d611cd2ec759ace4a01b10976b76bdacf2d3" - integrity sha512-M6mo8zlYAmwUleVS4ONoqNc8gNvQYQuBc1PZ89vmnr5FhlHcwIiJdK9Q8xjcARe8yUEKhy8F/VgkDs8pcTWY4A== +"@nrwl/workspace@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-18.3.0.tgz#ba2a03b3e9c0e4d3cc850c951bf1778ebfb8cd19" + integrity sha512-u0TlW2EcISfGaWug89MqCCD7DUeRfjtVnBHqbO3y3Oj19TB3QUNPhnfB/5Z+xybtAqn+bLRWZt2kpW8R5cVchQ== dependencies: - "@nx/web" "18.0.8" + "@nx/workspace" "18.3.0" -"@nrwl/workspace@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-18.0.8.tgz#86938241ac3e949d38b0b96dddda9e868ca77ac9" - integrity sha512-K1Fhfa1OqdhlZ46nPOFka9EBGt5bChLSIeJDs/JVZHyIlmGWeIQMy78oE2DPhrpW+ZowVxwVPdFgilESdfriQQ== +"@nrwl/workspace@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-19.0.4.tgz#fa0bfe2ed61461bf06b3d66a127aa4ffc90eeb30" + integrity sha512-aL2Z6sE2hmwnqcca2RZMbyq7gqrotvBivGONS1mRjpRaapJTkzsPDDaY3WDS/4Mmh7vbtoaINmJJeyJcllymiA== dependencies: - "@nx/workspace" "18.0.8" + "@nx/workspace" "19.0.4" "@nx-plus/vue@^14.1.0": version "14.1.0" @@ -2845,72 +5049,134 @@ tsconfig-paths-webpack-plugin "3.5.2" typescript "4.5.4" -"@nx/cypress@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/cypress/-/cypress-18.0.8.tgz#be970cfab2aaede9688f8c446d6451f78f518163" - integrity sha512-3ggo5EVaZw2fnUNGai7RibPp9xe3+FH+ismhiLamRQVWX3gVIixl1lrdqIpIaSwD4RLUs6guGSrP4X5G2VaBEg== +"@nx/cypress@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/cypress/-/cypress-18.3.0.tgz#6881a69335a7ab5a8fd70e67e11b46838e3b3a9e" + integrity sha512-zA3FyOe3A+TmHueVWqHaAien//FhADjwUXnvRlFun/+zGZeM/07clVaZnGMBgNttLbPuWE0HBQ4KnBXRC57bSA== dependencies: - "@nrwl/cypress" "18.0.8" - "@nx/devkit" "18.0.8" - "@nx/eslint" "18.0.8" - "@nx/js" "18.0.8" + "@nrwl/cypress" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/eslint" "18.3.0" + "@nx/js" "18.3.0" "@phenomnomnominal/tsquery" "~5.0.1" detect-port "^1.5.1" semver "^7.5.3" tslib "^2.3.0" -"@nx/devkit@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-18.0.8.tgz#4d7ce2f31984a9e61bd18e364f85d5f45779c8ac" - integrity sha512-df56bzmhF8yhVCCChe0ATjCsc9r9SNcpks5/bABGqR91vHVGfsz0H33RYO7P2jrPwFBRYnf+aWWFY1d6NpEdxw== +"@nx/devkit@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-18.3.0.tgz#5bdf7197d667762a423472eb548d7e066768d8e8" + integrity sha512-SgPPk+S8cEjNOzcvGiRPlNqAJVuPnspNrqFmBZ/ddBXQfhuS/TCr8Zi4MWEct45zd439acWDsuUVFoCxT51q4g== + dependencies: + "@nrwl/devkit" "18.3.0" + ejs "^3.1.7" + enquirer "~2.3.6" + ignore "^5.0.4" + semver "^7.5.3" + tmp "~0.2.1" + tslib "^2.3.0" + yargs-parser "21.1.1" + +"@nx/devkit@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-19.0.4.tgz#00c6ebee129bd874e143c36a65e86c3e885cdc9e" + integrity sha512-nsD0RaL61nZLHSJbog2XwxcI8bML5GlI69Z1k2rvd2zvylqdjNS4SXakMPl/Ar9xX2mAW3Qbup850V0jG87y/Q== dependencies: - "@nrwl/devkit" "18.0.8" + "@nrwl/devkit" "19.0.4" ejs "^3.1.7" enquirer "~2.3.6" ignore "^5.0.4" + minimatch "9.0.3" semver "^7.5.3" tmp "~0.2.1" tslib "^2.3.0" yargs-parser "21.1.1" -"@nx/eslint-plugin@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/eslint-plugin/-/eslint-plugin-18.0.8.tgz#3a3c6c00b6eb6660e2d4358d4aabc4205461804d" - integrity sha512-w6+z7AIq2d8zbXdRSoUSwBMymXwvdF7pT1HstpTj8wQ2bolefrclOhtM7Z0l2RIyWrS61HMk0kI4mdwbO5JbrA== +"@nx/esbuild@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/esbuild/-/esbuild-18.3.0.tgz#bd71405afb269b0887b92d88068b62c6aa509788" + integrity sha512-4OdOKZbn0OJBpVpcMbo1TC5xB8kqt3faiWqYHMWClnqfgB3p6q94F1+jG7FcQmNJ47m+7BrNRgUJ8jDOe+8l6w== + dependencies: + "@nrwl/esbuild" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" + chalk "^4.1.0" + fast-glob "3.2.7" + fs-extra "^11.1.0" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + +"@nx/eslint-plugin@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/eslint-plugin/-/eslint-plugin-18.3.0.tgz#479158c16ceb9eab2910b51ca7729c1a7fdd2d43" + integrity sha512-IAJ3I9G811uSmkJ2K3pGg1bsesm5AJW6u1zR5ie1C4qYO2ujhMhAcBXI9P/JUgY2WlO8EoH41PhRx4XUF29ttQ== dependencies: - "@nrwl/eslint-plugin-nx" "18.0.8" - "@nx/devkit" "18.0.8" - "@nx/js" "18.0.8" - "@typescript-eslint/type-utils" "^6.13.2" - "@typescript-eslint/utils" "^6.13.2" + "@nrwl/eslint-plugin-nx" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" + "@typescript-eslint/type-utils" "^7.3.0" + "@typescript-eslint/utils" "^7.3.0" chalk "^4.1.0" confusing-browser-globals "^1.0.9" jsonc-eslint-parser "^2.1.0" semver "^7.5.3" tslib "^2.3.0" -"@nx/eslint@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/eslint/-/eslint-18.0.8.tgz#dfbdfdd85e912762acca49ed30d4458b06a03c8b" - integrity sha512-Vp8LjN5rRbg76UReHToVtBibyb3izNpn6nAvFVtpzDSpyGnqb38F4/MD8hbHU3jHyP/xdVGmL3jyOJq5Uqkl8A== +"@nx/eslint@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/eslint/-/eslint-18.3.0.tgz#0d2929620158ecf225acccafd51902d09470d46c" + integrity sha512-inoFmDIycUsmIRY/iIQLxLKyJbdifyqYrsG/Hq6zmxsJOF6Q2R/Y88Zf9KET7EmN9+UEzBk70p4m8hOMVrC9eQ== + dependencies: + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" + "@nx/linter" "18.3.0" + eslint "^8.0.0" + tslib "^2.3.0" + typescript "~5.4.2" + +"@nx/eslint@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/eslint/-/eslint-19.0.4.tgz#de44f68c5d98afda05e76eae0d1cc28a4fb322de" + integrity sha512-fcXwKyOxsCO/NtUT+fggGOGeBqK9kHA8pw/5XBnQC/yCOtWGMCdTKps0QoU5FLBq0OoquoUObYovaqAV4Cqw5g== dependencies: - "@nx/devkit" "18.0.8" - "@nx/js" "18.0.8" - "@nx/linter" "18.0.8" + "@nx/devkit" "19.0.4" + "@nx/js" "19.0.4" + "@nx/linter" "19.0.4" eslint "^8.0.0" tslib "^2.3.0" - typescript "~5.3.2" + typescript "~5.4.2" + +"@nx/jest@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/jest/-/jest-18.3.0.tgz#a66b45561b3ede31a9c995cd3f63d4ff043a44ea" + integrity sha512-QsawUa3OIXCV+r/fxUJCzGKEbDqDKNEsC/wYCDKl48vJEU6+KEwRUZp604mIhvP4N377DwT9JGSzOEwaSPcKbg== + dependencies: + "@jest/reporters" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@nrwl/jest" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" + "@phenomnomnominal/tsquery" "~5.0.1" + chalk "^4.1.0" + identity-obj-proxy "3.0.0" + jest-config "^29.4.1" + jest-resolve "^29.4.1" + jest-util "^29.4.1" + minimatch "9.0.3" + resolve.exports "1.1.0" + tslib "^2.3.0" + yargs-parser "21.1.1" -"@nx/jest@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/jest/-/jest-18.0.8.tgz#da838792af673b85fa9e275bd11d7ec8ed5dbdbd" - integrity sha512-OE+1iB8b3z/kgdgkenXvlreBgPoe6A0b+OoN/0LmfKKC5jddTUISLTgb96L8G76XPTwFEcGPRm1BcdxAxS0M3g== +"@nx/jest@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/jest/-/jest-19.0.4.tgz#e69a95db531a0211588b0df70c93f2f77b0ac88c" + integrity sha512-OH/brJQ2mCqN9HU3Xjc2i5kPH+6wSpjWwxXQ+rbA45hKiE7kCTjwrvEFKindjBtsRJwI/PVdcjEc0d8zbDW73w== dependencies: "@jest/reporters" "^29.4.1" "@jest/test-result" "^29.4.1" - "@nrwl/jest" "18.0.8" - "@nx/devkit" "18.0.8" - "@nx/js" "18.0.8" + "@nrwl/jest" "19.0.4" + "@nx/devkit" "19.0.4" + "@nx/js" "19.0.4" "@phenomnomnominal/tsquery" "~5.0.1" chalk "^4.1.0" identity-obj-proxy "3.0.0" @@ -2920,11 +5186,12 @@ minimatch "9.0.3" resolve.exports "1.1.0" tslib "^2.3.0" + yargs-parser "21.1.1" -"@nx/js@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/js/-/js-18.0.8.tgz#f512227f721b36b790cf4dd7137035c91b3fcedd" - integrity sha512-buBHn0xUTF30Ifm6uFABltX0qwDxrxbmfTngPqs8BxFj9btYrlRz2XqtoAST/4L9AYheuY4tnwXZQvBkRh6rrw== +"@nx/js@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/js/-/js-18.3.0.tgz#d8c22e2c7f858a76fac8868b9d14118af8db838e" + integrity sha512-ApxC3FdZ9ATnE6Qz932B3/L9ZqdI6pIxB+1R5J/jMK/InNlPnNStGp1+dGe5J3aQ0nWusSW9I+FjpqRMTZazvw== dependencies: "@babel/core" "^7.23.2" "@babel/plugin-proposal-decorators" "^7.22.7" @@ -2933,9 +5200,9 @@ "@babel/preset-env" "^7.23.2" "@babel/preset-typescript" "^7.22.5" "@babel/runtime" "^7.22.6" - "@nrwl/js" "18.0.8" - "@nx/devkit" "18.0.8" - "@nx/workspace" "18.0.8" + "@nrwl/js" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/workspace" "18.3.0" "@phenomnomnominal/tsquery" "~5.0.1" babel-plugin-const-enum "^1.0.1" babel-plugin-macros "^2.8.0" @@ -2957,111 +5224,283 @@ tsconfig-paths "^4.1.2" tslib "^2.3.0" -"@nx/linter@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/linter/-/linter-18.0.8.tgz#7efd8241cf50ace1cf5ff2aa79a69ee2f64abaf9" - integrity sha512-R+hopeExRArwFDtfz8hzODf8EId24ZuSy1gB01yuKDGVFNR5WmY96v5MAjp06ZDwv/tGvTwzH+O2akc7/20VeA== - dependencies: - "@nx/eslint" "18.0.8" - -"@nx/nx-darwin-arm64@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.0.8.tgz#0c51e43cd7dbd7a25e63562aafdecc478661801f" - integrity sha512-B2vX90j1Ex9Mki/Fai45UJ0r7mPc/xLBzQYQ9MFI2XoUXKhYl5BVBfJ+EbJ2PBcIXAnp44qY0wyxEpp+8Glxcg== - -"@nx/nx-darwin-x64@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-18.0.8.tgz#df8d5ea87565afbf86dfe46bd1ea646ed5721e62" - integrity sha512-nC172j4LwOqc22BtJGsrjPYGhZ6EFXhYi0ceb6yzEA1Z32Wl98OXbAcbbhyEcuL3iYI9VrZgzAAzIUo7l4injw== - -"@nx/nx-freebsd-x64@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-18.0.8.tgz#415085b231eab5a3887e55fe5e110be681a441a8" - integrity sha512-Qoz668WMB6nxdMFG5X88B7W72+d5K/95XEFKY2022EPm88DQFFcJAfdkMrRkeO3yBJtwLAAK+Jyni9uAfOXzGQ== - -"@nx/nx-linux-arm-gnueabihf@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-18.0.8.tgz#b7aeffb7c10ca6f4169ae01179bd14891097bddd" - integrity sha512-0RTuJTaAmE7Xjc0P0DIbbYEhPGBILCii2nOz6vwTEzIqxSMgXW4T1g1zSDKCiUUyS6HVffGvCTNvuHuoYY2DMg== - -"@nx/nx-linux-arm64-gnu@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-18.0.8.tgz#8b52f40e79056ba8d1af46f7787c0d95caa55b80" - integrity sha512-fmwsrDeeY44f6cCnfrXNuvFEzqvD/A5yg3TVwZoKldWRAG5gexj4AWpBHqgGTcCj6ky1NGxnlaktKC5geGhJhA== - -"@nx/nx-linux-arm64-musl@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-18.0.8.tgz#725258e5c15c1f9f788bab0af30a56e29beb85d1" - integrity sha512-jz1dzQlrfZteJdsEJ1MbjI7m2jkBLhLe5y9x+96/KgmJbCV7LD9RLevWIzz7FDuhfJziMOoSrGdaW47G13p/Fw== - -"@nx/nx-linux-x64-gnu@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.0.8.tgz#98c5456e1cfdaad65bfae0c188a2f8676b634f5d" - integrity sha512-eq2AAZN4fsjhABtU76eroFHcNK6QWo4eMAH7tcZUoGLwfBAo+wPYggxm9LNZ5weKVxwqySHavlXd5rNA26WrbA== - -"@nx/nx-linux-x64-musl@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-18.0.8.tgz#53bfaf2432d1a8988995136e45d22b278927b003" - integrity sha512-FBHVJ0DtBqQynbQImg1kc9/WfRGSvbRNzaqI2rO/zO0j2BeT9BQ8byTn2EiMBxz72LSbqEmtQtqe5w50hAsKcA== - -"@nx/nx-win32-arm64-msvc@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-18.0.8.tgz#dd70a40a5f8345587eee3950adcb3d36aa396c61" - integrity sha512-qphQIIfwAR03s7ifPVc0XhjdOeep2hOoZN2jN5ShG1QD/DIipNnMrRK21M6JcoP7soRPpkJFlI5Yaleh9/EJhg== - -"@nx/nx-win32-x64-msvc@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.0.8.tgz#fe1ae3d1b1eec2ef4291a700500cc96bbfb81cec" - integrity sha512-XP8hle+cPNH5n18iTM7l0q07zEdvoPcHYVr5IoYOA54Ke9ZUxau4owUeok2HhLr61o2u0CTwf1vWoV+Y1AUAdg== - -"@nx/playwright@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/playwright/-/playwright-18.0.8.tgz#2c42111778c479a532eb9c3d1575598338afd71b" - integrity sha512-KbtZh/gGshIynicZB6oCl/6tgtrCoVQZfx6Fz9mVGajXNsiemSAHwopW749NjzoBne5vO7shWhRaDcCrDKDOOw== - dependencies: - "@nx/devkit" "18.0.8" - "@nx/eslint" "18.0.8" - "@nx/js" "18.0.8" +"@nx/js@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/js/-/js-19.0.4.tgz#f1c0e1e30b1de491e3c9d72de4bd281ec9ae82b5" + integrity sha512-u6ZGuEi/GuTUU7XA2cyP+XHGvN3E1imbnoU5gv+QfjHRAQEp1mBdY5byGqx2FVsiA7yFoi60ZwSZLgIcl6V0rw== + dependencies: + "@babel/core" "^7.23.2" + "@babel/plugin-proposal-decorators" "^7.22.7" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-runtime" "^7.23.2" + "@babel/preset-env" "^7.23.2" + "@babel/preset-typescript" "^7.22.5" + "@babel/runtime" "^7.22.6" + "@nrwl/js" "19.0.4" + "@nx/devkit" "19.0.4" + "@nx/workspace" "19.0.4" + babel-plugin-const-enum "^1.0.1" + babel-plugin-macros "^2.8.0" + babel-plugin-transform-typescript-metadata "^0.3.1" + chalk "^4.1.0" + columnify "^1.6.0" + detect-port "^1.5.1" + fast-glob "3.2.7" + fs-extra "^11.1.0" + ignore "^5.0.4" + js-tokens "^4.0.0" + minimatch "9.0.3" + npm-package-arg "11.0.1" + npm-run-path "^4.0.1" + ora "5.3.0" + semver "^7.5.3" + source-map-support "0.5.19" + ts-node "10.9.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + +"@nx/linter@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/linter/-/linter-18.3.0.tgz#08f68a82a352baf47a6cab793d5e5d9196e01ca6" + integrity sha512-ydTP8MFNE+KzWvIVxg7IJIMcjkt02ehwyudnkirEu5hFOUY6uA/ZQtOEk7y2ESDuF19LR11wVHPaeeSCG94Cbg== + dependencies: + "@nx/eslint" "18.3.0" + +"@nx/linter@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/linter/-/linter-19.0.4.tgz#1c6a373426b9038aa582da6295bec06e6534d4ac" + integrity sha512-Rdw8zUcMVG95v5EGTqYo9LkOj0XlvHhhEbKO2ts2nRwuFWDcTZGXllfyNmN0hUhzrtBivsKpeTu1dlItLJR2zA== + dependencies: + "@nx/eslint" "19.0.4" + +"@nx/node@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/node/-/node-18.3.0.tgz#ef5f0800bc5a1dfce682b9c15eea838a548324a5" + integrity sha512-zUUdIalE5lTHdubBPUpmyGXPh7rUxlJgo/8qiF+0uve2PTn/bsL+wYlClhdzYT73m0AUOPFL8wh4dEF2LPGD+w== + dependencies: + "@nrwl/node" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/eslint" "18.3.0" + "@nx/jest" "18.3.0" + "@nx/js" "18.3.0" + tslib "^2.3.0" + +"@nx/node@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/node/-/node-19.0.4.tgz#96858c14aa8a2b18010c850ceb1598e97b268f80" + integrity sha512-8C4dL+1vPdF298sf1t06uhZ/K+cLQIQZ7GUO39DyKmv11CAbYenpRLDaa9PlzAZjAslezzf6kd/YQFIG/17Q0w== + dependencies: + "@nrwl/node" "19.0.4" + "@nx/devkit" "19.0.4" + "@nx/eslint" "19.0.4" + "@nx/jest" "19.0.4" + "@nx/js" "19.0.4" + tslib "^2.3.0" + +"@nx/nx-darwin-arm64@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.3.0.tgz#6ddf33e6e24d180fc5bcd46685aee00406be7ea6" + integrity sha512-zei4C7nSCAzhigAX+3wLcHg1bokTsa/qo2OElkBiHAxs3FF7nqMLAuk0WFYi3nkvXTgiN1uEl0mOni+JPKV2vA== + +"@nx/nx-darwin-arm64@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.0.4.tgz#4512eca7e214f92450df7f0b6d05c24e198b6363" + integrity sha512-EwTMKVFdMF42b+DG3ACtrGVE3iiAgOw+VJ4Vekm59+ZkTg6GrZly2VNbthoNSJd6/uPQssoljx36NZH953ieBw== + +"@nx/nx-darwin-x64@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-18.3.0.tgz#5a92242f70837c5fd2ce0e27910c0bebc6a71835" + integrity sha512-nww//ea6WEfDTnqbdbCinWRhjyUJkSSnW9QgBh/Brt6DevZ7TFWfGdxD+s45pmMLFTFMgRjptRJrW/WhgmDAGg== + +"@nx/nx-darwin-x64@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-19.0.4.tgz#677a4210c5bc53f8dcebd0072910438d2d684c8e" + integrity sha512-W+SVaYOHWRHcws7wZVcWyxoT57r1qXLMUBvpTVBf5PsVfsI+t9sINwzZjcXWaGNVcPGrVYUZF6Cp3/exkPNUBw== + +"@nx/nx-freebsd-x64@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-18.3.0.tgz#fcdb6dbcbc8e5d5332c157420e84e666ed2e5501" + integrity sha512-u+XB6NQcsi7u3zhdhgJK9ZaUkzXl52WNgtDoG/6tsmbh10plypGnw+yPSKYMqv3HDzqDA76hliIFoedDbZmHFQ== + +"@nx/nx-freebsd-x64@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.0.4.tgz#7ef2be4044d9a1664e4b27554c4aecc6a98c56e7" + integrity sha512-8Wl2+TOXiRDLbI8mwsbx1sHQLKAaNvfTm2e5Kf+4ay4W/UsrHONRDRA4d/LhMOLQMo+2+q2q+u8DziqT0w0Vaw== + +"@nx/nx-linux-arm-gnueabihf@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-18.3.0.tgz#8c397d16ec040214d23ba0c867182c915d785795" + integrity sha512-nsjiJDq2B2m9NN7shJ8z/4A7bFUYGJdxk1RR6hVXY75Kpbh3HGh+fdKJrpqRzYUUmqxW/X7TRG2UD6T5lnNjWA== + +"@nx/nx-linux-arm-gnueabihf@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.0.4.tgz#8fc485f2841a2781099ab1f3f5c7ebd8b78094c0" + integrity sha512-C3PBsyNM5Npq8G8h/WHjUwwlKZpfWK4tK1ZeNseb6LtoNIgNF0PVrJQERqXABt29lanoQ4SeJ8RPgppB3xgCwg== + +"@nx/nx-linux-arm64-gnu@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-18.3.0.tgz#4f6bf538d6a0e0539d182f5a7dc2650017287cf0" + integrity sha512-baY3U0PudlAXHDzkJ+KdSfIcfFGKuBYXIXR1M18+Syq1kD9HDZ+sRVmosYpxVghrncN4UrcNvF/H7lgZo9x24Q== + +"@nx/nx-linux-arm64-gnu@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.0.4.tgz#ec3ac667b80582b07467f2b4c4d97db5f4d27d0e" + integrity sha512-d7gJv/QlaaBKTHpN+DmnQvo1FBNOGfh9b819WMaNXgDLSNpw9CpaOBZPbPgduee3OaGwbfWmll8VDYzUZgKWuw== + +"@nx/nx-linux-arm64-musl@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-18.3.0.tgz#1d23ee65246b8c9f24d8e82802ed59cd5525d1f3" + integrity sha512-nuKU4ehdKThq+Tzph2KXz2p39oBv8IrJQBONSAFzJ4zS0E/rNk2fKBeTBoqn1Psh2sNMYM8ZdlvxFK7pBmStQg== + +"@nx/nx-linux-arm64-musl@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.0.4.tgz#b4bfa2465dbd9c2cccc6297f5ebb890f6865a0c0" + integrity sha512-lQ76O4AtXAQJ6r1MdVDVp4GG+o2vylWFjcyZvZpclhjag+fWKSdO0igL/14HsqNwCPmcPtaHmgqQNlw3MMtL3w== + +"@nx/nx-linux-x64-gnu@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.3.0.tgz#d0a6c1aa9abba5ae724d05cf04ef7573116d9960" + integrity sha512-Pm7Q1hjKBJ3DFfnCLAtJVm13SkIushO3rPUdsDg5xZzOp59igNxrX2wJlwfi7U8dZMEZUPG0N1BIR3o7eEBxpQ== + +"@nx/nx-linux-x64-gnu@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.0.4.tgz#cebb20b39e670b657d4d6f44dfa45b0af45ac9a3" + integrity sha512-1K95WMdKHM4pMACzsO9m9TWqSXwL5cg9/1UuS9LUKhjY/bX2y3iTtzT0tFBptCVzRVLZG8wAZphxwQfBIQvnCQ== + +"@nx/nx-linux-x64-musl@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-18.3.0.tgz#73404e3a9a407cb32a1d0c7e26633737146d2144" + integrity sha512-VrspyfjIto0PtAqpjG3k8ueWsnqIOUp1gXBmlzYw0N4mjPldlhb258q1Kqyt1ykWLW79TqCjPblC6xHuOciKzQ== + +"@nx/nx-linux-x64-musl@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.0.4.tgz#0d314ed7103540ef3245008099116a18d5b73a04" + integrity sha512-iZ+TH/qT2R6nb+bqL8oJDDeUUEJmzYxtacFlf5yLjaiG5nvOxq7cu/lUw/LEqT+BUgK33T7acr3BDC0/q2bFZQ== + +"@nx/nx-win32-arm64-msvc@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-18.3.0.tgz#7c6a930c5e2fe5e0c4c0dd3ce04e517cbf00bbba" + integrity sha512-7C+Rk17u/CtcYq/LyG8b27MmuxjQOAqZ1yWPP5RHRr0HGB00kILkItmejs/CJAJqybPtydTR0hiF7xs7lcVOHw== + +"@nx/nx-win32-arm64-msvc@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.0.4.tgz#83fce511a288031d577e47652aeface9f16e4b4b" + integrity sha512-YiRyGZecH4hIy5shZz8SNX5NwY+dZC3Xs09QlMeLKNhf6klfmjJYNtd+9250V4cjJS3opKYf08uG4x+EtuEB5A== + +"@nx/nx-win32-x64-msvc@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.3.0.tgz#7cfb7033e24056a9177ccedd2a2f10906d3f7fbe" + integrity sha512-tRW2VZzwmdODaRXNgBJBSycVgLY269c3EwJDOCIPDIgFMTdClZNLmZbk4b7FfzyT7ezwQOD/3JgKJS6GzJdw8w== + +"@nx/nx-win32-x64-msvc@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.0.4.tgz#1cf618594e442385d6bc33411acccb7759546546" + integrity sha512-eHEdPjV0GlblyBM501xfe47tPRzugw2U+YOkZh++Ago9MDOrs/ULS9+RM3NhvZl2WnkpNYDbQMjzbQ0r7rxlTA== + +"@nx/playwright@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/playwright/-/playwright-18.3.0.tgz#e85d93a3e97261d8cd41e1fa364f640056fd422c" + integrity sha512-PZyPNcu0MeWgqiUIND+2Z96KAJ1ik6mxc9n+gv0CVMIy8bm1+6MxvvJF4Hh/vooPZyOb6ROlemygUOfI2xpu1w== + dependencies: + "@nx/devkit" "18.3.0" + "@nx/eslint" "18.3.0" + "@nx/js" "18.3.0" "@phenomnomnominal/tsquery" "~5.0.1" minimatch "9.0.3" tslib "^2.3.0" -"@nx/vite@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/vite/-/vite-18.0.8.tgz#b91513a99e9805040b9a2d5252ee1cb78806c07f" - integrity sha512-DOGOMFEi+UiTklE7By29CoYDvsYJYFEHShF4x55eIri329hdjiTFUBPjuRn/M0U8rGsV3HNtghLBStPhCM+1PQ== +"@nx/vite@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/vite/-/vite-18.3.0.tgz#081be38a2075a8dac20e10e252c76d47d2ec5836" + integrity sha512-u/4UlfgRaPMK7OHpJ40YGLwylPltCA7RSWN573I8sic/y8YfZgMtZ38RymVDXO//EWwy/lRqsMlaUDD2FTm+Jw== dependencies: - "@nrwl/vite" "18.0.8" - "@nx/devkit" "18.0.8" - "@nx/js" "18.0.8" + "@nrwl/vite" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" "@phenomnomnominal/tsquery" "~5.0.1" "@swc/helpers" "~0.5.0" enquirer "~2.3.6" tsconfig-paths "^4.1.2" -"@nx/web@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/web/-/web-18.0.8.tgz#de8620a5264c77a664073e5c76e5d73d278085b8" - integrity sha512-VpRGZ+Nxe1dldOT+dSWjjbvGO2oN/Gj0Lc6AcmKYSeBqbMnOfStylFl2vMEXISe2Flx3mHzzbRfKDC+z4U/kQA== +"@nx/web@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/web/-/web-18.3.0.tgz#1d2f7df00e261f1707807a327a933afde8bde286" + integrity sha512-uEX0x96CXtiAD27XBTiFt1OV0seFuy18iJhm0wvS90VDVwAtqquBwBNX3UexHyCrIHn3qGr5tjsRBdpzQv3eCA== dependencies: - "@nrwl/web" "18.0.8" - "@nx/devkit" "18.0.8" - "@nx/js" "18.0.8" + "@nrwl/web" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" chalk "^4.1.0" detect-port "^1.5.1" http-server "^14.1.0" tslib "^2.3.0" -"@nx/workspace@18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@nx/workspace/-/workspace-18.0.8.tgz#5d4b473365ad2f3df77214ba75872765208c6ede" - integrity sha512-y4s+PUgCWKd8tyvTjM0M05usIbPvl5wcTmoU9R4UriPAmNYzttvKYjweNcPJKsqdng1iwx7eECY1SmiOVRaTag== +"@nx/webpack@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/webpack/-/webpack-18.3.0.tgz#890ff7ddf418bb713b661113ea34faf76285b44c" + integrity sha512-Lue+64LRdMJ+EwSDht/kkDMh8aPFRKN1+RQWhGp/ZZHeoimEeHAAXQu7WQ/QDzl+w6vWq+lQCrXENEf8lLQFhQ== + dependencies: + "@babel/core" "^7.23.2" + "@nrwl/webpack" "18.3.0" + "@nx/devkit" "18.3.0" + "@nx/js" "18.3.0" + ajv "^8.12.0" + autoprefixer "^10.4.9" + babel-loader "^9.1.2" + browserslist "^4.21.4" + chalk "^4.1.0" + copy-webpack-plugin "^10.2.4" + css-loader "^6.4.0" + css-minimizer-webpack-plugin "^5.0.0" + fork-ts-checker-webpack-plugin "7.2.13" + less "4.1.3" + less-loader "11.1.0" + license-webpack-plugin "^4.0.2" + loader-utils "^2.0.3" + mini-css-extract-plugin "~2.4.7" + parse5 "4.0.0" + postcss "^8.4.14" + postcss-import "~14.1.0" + postcss-loader "^6.1.1" + rxjs "^7.8.0" + sass "^1.42.1" + sass-loader "^12.2.0" + source-map-loader "^3.0.0" + style-loader "^3.3.0" + stylus "^0.59.0" + stylus-loader "^7.1.0" + terser-webpack-plugin "^5.3.3" + ts-loader "^9.3.1" + tsconfig-paths-webpack-plugin "4.0.0" + tslib "^2.3.0" + webpack "^5.80.0" + webpack-dev-server "^4.9.3" + webpack-node-externals "^3.0.0" + webpack-subresource-integrity "^5.1.0" + +"@nx/workspace@18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@nx/workspace/-/workspace-18.3.0.tgz#df704109050152286acb416d1f41677bd1694196" + integrity sha512-gW5cR7Toki8HzO8uhEmjQYCRT17rOLcTcMSSlX2Y7VorgtL8+kUlVpqSsuGFBWiXsuSiMnATiXtHesDbSBKfYw== + dependencies: + "@nrwl/workspace" "18.3.0" + "@nx/devkit" "18.3.0" + chalk "^4.1.0" + enquirer "~2.3.6" + nx "18.3.0" + tslib "^2.3.0" + yargs-parser "21.1.1" + +"@nx/workspace@19.0.4": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@nx/workspace/-/workspace-19.0.4.tgz#042c4e7510b44633f438aeee4da0e72fc4e92c89" + integrity sha512-45dTDjeRDjfKG66h7E8PM13LXjnfUaTxDwYT9AqV90Uqw1EnGXte+KBNoG0IZsD3yoPpqTUIdZssl0twnVBlvQ== dependencies: - "@nrwl/workspace" "18.0.8" - "@nx/devkit" "18.0.8" + "@nrwl/workspace" "19.0.4" + "@nx/devkit" "19.0.4" chalk "^4.1.0" enquirer "~2.3.6" - nx "18.0.8" + nx "19.0.4" tslib "^2.3.0" yargs-parser "21.1.1" @@ -3165,6 +5604,33 @@ resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== +"@openzeppelin/contracts@3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" + integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== + +"@openzeppelin/defender-base-client@1.54.2": + version "1.54.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/defender-base-client/-/defender-base-client-1.54.2.tgz#60d3550d7bef4e75ac35d7de7648ecbb0ef2ad63" + integrity sha512-0k2Md6WKKkLTbsygz4UYWojJAkgrNLrishmosUIBCjaiGcAXMopgnRgX6V4WjnWkyI8RVEqb9H6IZY+8BNk6Bw== + dependencies: + amazon-cognito-identity-js "^6.0.1" + async-retry "^1.3.3" + axios "^1.4.0" + lodash "^4.17.19" + node-fetch "^2.6.0" + +"@openzeppelin/defender-relay-client@^1.54.2": + version "1.54.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/defender-relay-client/-/defender-relay-client-1.54.2.tgz#82f4258eab5000a7dae87b15b528bcb4b966de96" + integrity sha512-S93RRaywwfNMbHtbRWisw7ZKBCzHtdoPkNC4K8QaZ8usbJQP74v6unOFJ19s62iVi++6UO5QqqUkxdcFsfe7Dg== + dependencies: + "@openzeppelin/defender-base-client" "1.54.2" + amazon-cognito-identity-js "^6.0.1" + axios "^1.4.0" + lodash "^4.17.19" + node-fetch "^2.6.0" + "@parcel/watcher-android-arm64@2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.0.tgz#9c93763794153e4f76920994a423b6ea3257059d" @@ -3271,6 +5737,16 @@ dependencies: esquery "^1.4.0" +"@pinata/sdk@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@pinata/sdk/-/sdk-2.1.0.tgz#d61aa8f21ec1206e867f4b65996db52b70316945" + integrity sha512-hkS0tcKtsjf9xhsEBs2Nbey5s+Db7x5rlOH9TaWHBXkJ7IwwOs2xnEDigNaxAHKjYAwcw+m2hzpO5QgOfeF7Zw== + dependencies: + axios "^0.21.1" + form-data "^2.3.3" + is-ipfs "^0.6.0" + path "^0.12.7" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -3283,6 +5759,19 @@ dependencies: playwright "1.42.1" +"@pmmmwh/react-refresh-webpack-plugin@^0.5.7": + version "0.5.13" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.13.tgz#02338a92a92f541a5189b97e922caf3215221e49" + integrity sha512-odZVYXly+JwzYri9rKqqUAk0cY6zLpv4dxoKinhoJNShV36Gpxf+CyDIILJ4tYsJ1ZxIWs233Y39iVnynvDA/g== + dependencies: + ansi-html-community "^0.0.8" + core-js-pure "^3.23.3" + error-stack-parser "^2.0.6" + html-entities "^2.1.0" + loader-utils "^2.0.4" + schema-utils "^3.0.0" + source-map "^0.7.3" + "@polka/url@^1.0.0-next.24": version "1.0.0-next.25" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" @@ -3293,6 +5782,11 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@prisma/prisma-fmt-wasm@^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085": + version "4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" + resolved "https://registry.yarnpkg.com/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085.tgz#030f8a4448892c345b3c5c0558ca0ebf4642f3de" + integrity sha512-zYz3rFwPB82mVlHGknAPdnSY/a308dhPOblxQLcZgZTDRtDXOE1MgxoRAys+jekwR4/bm3+rZDPs1xsFMsPZig== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -3453,10 +5947,10 @@ "@safe-global/safe-core-sdk-types" "^2.3.0" node-fetch "^2.6.6" -"@safe-global/protocol-kit@^1.0.1": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-1.3.0.tgz#fb84a3797a4afc74ac7fc218e796037d6e3cc3cc" - integrity sha512-zBhwHpaUggywmnR1Xm5RV22DpyjmVWYP3pnOl4rcf9LAc1k7IVmw6WIt2YVhHRaWGxVYMd4RitJX8Dx2+8eLZQ== +"@safe-global/protocol-kit@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-1.2.0.tgz#5501fa0e2a1b4ad03cd5a4ee12bb9e799e1dd5a7" + integrity sha512-drU2uK30AZ4tqI/9ER7PGMD/lZp/5B9T02t+noTk7WF9Xb7HxskJd8GNU01KE55oyH31Y0AfXaE68H/f9lYa4A== dependencies: "@ethersproject/address" "^5.7.0" "@ethersproject/bignumber" "^5.7.0" @@ -3467,7 +5961,6 @@ web3 "^1.8.1" web3-core "^1.8.1" web3-utils "^1.8.1" - zksync-web3 "^0.14.3" "@safe-global/safe-apps-provider@0.18.1": version "0.18.1" @@ -3519,11 +6012,38 @@ resolved "https://registry.yarnpkg.com/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.15.0.tgz#2a99e7eca7aecfad1f5e00744ffdd949cefa4f6a" integrity sha512-zAzhPgUwzdp89ZrZwCAOImUyAQMQE0LQKcK4vLO5eMbfAcNOxz5g4eVdBRBRa+kVXxjyW5wii58ZlGaYUVBa7g== +"@sapphire/async-queue@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.2.tgz#2982dce16e5b8b1ea792604d20c23c0585877b97" + integrity sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg== + +"@sapphire/shapeshift@^3.9.7": + version "3.9.7" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz#43e23243cac8a0c046bf1e73baf3dbf407d33a0c" + integrity sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@3.5.3", "@sapphire/snowflake@^3.5.3": + version "3.5.3" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.3.tgz#0c102aa2ec5b34f806e9bc8625fc6a5e1d0a0c6a" + integrity sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ== + "@scure/base@^1.1.3", "@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.4": version "1.1.5" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157" integrity sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ== +"@scure/bip32@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" + integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== + dependencies: + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + "@scure/bip32@1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" @@ -3542,6 +6062,14 @@ "@noble/hashes" "~1.3.2" "@scure/base" "~1.1.4" +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" + "@scure/bip39@1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" @@ -3609,140 +6137,672 @@ resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.14.2.tgz#6750c46fa4836b46ea48556b19f5e6789a428a47" integrity sha512-HgOFWYdq87lSmeVW1w8K2Vf2DGzRPvKzHTajZYLTPlrZ1jbajq9vwuqhrJ9AnDkjl0mjyzSPEy3ZTeG1Z7uRNA== dependencies: - "@babel/core" "7.18.5" - "@sentry/babel-plugin-component-annotate" "2.14.2" - "@sentry/cli" "^2.22.3" - dotenv "^16.3.1" - find-up "5.0.0" - glob "9.3.2" - magic-string "0.27.0" - unplugin "1.0.1" + "@babel/core" "7.18.5" + "@sentry/babel-plugin-component-annotate" "2.14.2" + "@sentry/cli" "^2.22.3" + dotenv "^16.3.1" + find-up "5.0.0" + glob "9.3.2" + magic-string "0.27.0" + unplugin "1.0.1" + +"@sentry/cli-darwin@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.28.6.tgz#83f9127de77e2a2d25eb143d90720b3e9042adc1" + integrity sha512-KRf0VvTltHQ5gA7CdbUkaIp222LAk/f1+KqpDzO6nB/jC/tL4sfiy6YyM4uiH6IbVEudB8WpHCECiatmyAqMBA== + +"@sentry/cli-linux-arm64@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.28.6.tgz#6bb660e5d8145270e287a9a21201d2f9576b0634" + integrity sha512-caMDt37FI752n4/3pVltDjlrRlPFCOxK4PHvoZGQ3KFMsai0ZhE/0CLBUMQqfZf0M0r8KB2x7wqLm7xSELjefQ== + +"@sentry/cli-linux-arm@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.28.6.tgz#73d466004ac445d9258e83a7b3d4e0ee6604e0bd" + integrity sha512-ANG7U47yEHD1g3JrfhpT4/MclEvmDZhctWgSP5gVw5X4AlcI87E6dTqccnLgvZjiIAQTaJJAZuSHVVF3Jk403w== + +"@sentry/cli-linux-i686@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.28.6.tgz#f7175ca639ee05cf12d808f7fc31d59d6e2ee3b9" + integrity sha512-Tj1+GMc6lFsDRquOqaGKXFpW9QbmNK4TSfynkWKiJxdTEn5jSMlXXfr0r9OQrxu3dCCqEHkhEyU63NYVpgxIPw== + +"@sentry/cli-linux-x64@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.28.6.tgz#df0af8d6c8c8c880eb7345c715a4dfa509544a40" + integrity sha512-Dt/Xz784w/z3tEObfyJEMmRIzn0D5qoK53H9kZ6e0yNvJOSKNCSOq5cQk4n1/qeG0K/6SU9dirmvHwFUiVNyYg== + +"@sentry/cli-win32-i686@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.28.6.tgz#0df19912d1823b6ec034b4c4c714c7601211c926" + integrity sha512-zkpWtvY3kt+ogVaAbfFr2MEkgMMHJNJUnNMO8Ixce9gh38sybIkDkZNFnVPBXMClJV0APa4QH0EwumYBFZUMuQ== + +"@sentry/cli-win32-x64@2.28.6": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.28.6.tgz#2344a206be3b555ec6540740f93a181199962804" + integrity sha512-TG2YzZ9JMeNFzbicdr5fbtsusVGACbrEfHmPgzWGDeLUP90mZxiMTjkXsE1X/5jQEQjB2+fyfXloba/Ugo51hA== + +"@sentry/cli@^2.22.3": + version "2.28.6" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.28.6.tgz#645f31b9e742e7bf7668c8f867149359e79b8123" + integrity sha512-o2Ngz7xXuhwHxMi+4BFgZ4qjkX0tdZeOSIZkFAGnTbRhQe5T8bxq6CcQRLdPhqMgqvDn7XuJ3YlFtD3ZjHvD7g== + dependencies: + https-proxy-agent "^5.0.0" + node-fetch "^2.6.7" + progress "^2.0.3" + proxy-from-env "^1.1.0" + which "^2.0.2" + optionalDependencies: + "@sentry/cli-darwin" "2.28.6" + "@sentry/cli-linux-arm" "2.28.6" + "@sentry/cli-linux-arm64" "2.28.6" + "@sentry/cli-linux-i686" "2.28.6" + "@sentry/cli-linux-x64" "2.28.6" + "@sentry/cli-win32-i686" "2.28.6" + "@sentry/cli-win32-x64" "2.28.6" + +"@sentry/core@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" + integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/core@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.102.1.tgz#855d37b6bba9986a9380864c823e696d3fc5aa01" + integrity sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg== + dependencies: + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry/hub@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" + integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== + dependencies: + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/minimal@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" + integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" + integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== + dependencies: + "@sentry/core" "5.30.0" + "@sentry/hub" "5.30.0" + "@sentry/tracing" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/replay@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.102.1.tgz#d6c17332d14dc312b124bbbda8f35d6a982b893c" + integrity sha512-HR/j9dGIvbrId8fh8mQlODx7JrhRmawEd9e9P3laPtogWCg/5TI+XPb2VGSaXOX9VWtb/6Z2UjHsaGjgg6YcuA== + dependencies: + "@sentry-internal/tracing" "7.102.1" + "@sentry/core" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry/tracing@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" + integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/types@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" + integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== + +"@sentry/types@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.102.1.tgz#18c35f32ecbd12afb9860ca2de7bfff542d10b27" + integrity sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA== + +"@sentry/utils@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" + integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== + dependencies: + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/utils@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.102.1.tgz#45ddcdf2e700d40160347bbdf4233aff3179d398" + integrity sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg== + dependencies: + "@sentry/types" "7.102.1" + +"@sentry/vite-plugin@^2.7.1": + version "2.14.2" + resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.14.2.tgz#f17cbd5526a95de3d8a7995b4cd90e2c8a548bf1" + integrity sha512-t8IiRZGxivtODgabjgHlgUhOBEIJdOclJGUKLAJjJqPtYeKjPzxYOo/Z5yt7k1rhBAaMhFk3whW5o7SOq4KVOA== + dependencies: + "@sentry/bundler-plugin-core" "2.14.2" + unplugin "1.0.1" + +"@sentry/vue@^7.71.0": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/vue/-/vue-7.102.1.tgz#3dea7987dae7338a428a525f94b44e29d90ff6b1" + integrity sha512-7sTrdAe3EL45MaA44mAgSPRg7jQ/CE6LifHl+62hjchpzsh+W+xWsN+31hbvm9ek6v/gNnQAlxyAXqXBRWtrlQ== + dependencies: + "@sentry/browser" "7.102.1" + "@sentry/core" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@smithy/abort-controller@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.0.0.tgz#5815f5d4618e14bf8d031bb98a99adabbb831168" + integrity sha512-p6GlFGBt9K4MYLu72YuJ523NVR4A8oHlC5M2JO6OmQqN8kAc/uh1JqLE+FizTokrSJGg0CSvC+BrsmGzKtsZKA== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader-native@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz#f1104b30030f76f9aadcbd3cdca4377bd1ba2695" + integrity sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg== + dependencies: + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz#e5d3b04e9b273ba8b7ede47461e2aa96c8aa49e0" + integrity sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA== + dependencies: + tslib "^2.6.2" + +"@smithy/config-resolver@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.0.tgz#d37b31e3202c5ce54d9bd2406dcde7c7b5073cbd" + integrity sha512-2GzOfADwYLQugYkKQhIyZyQlM05K+tMKvRnc6eFfZcpJGRfKoMUMYdPlBKmqHwQFXQKBrGV6cxL9oymWgDzvFw== + dependencies: + "@smithy/node-config-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + tslib "^2.6.2" + +"@smithy/core@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.0.1.tgz#8a7ac8faa0227912ce260bc3f976a5e254323920" + integrity sha512-rcMkjvwxH/bER+oZUPR0yTA0ELD6m3A+d92+CFkdF6HJFCBB1bXo7P5pm21L66XwTN01B6bUhSCQ7cymWRD8zg== + dependencies: + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-retry" "^3.0.1" + "@smithy/middleware-serde" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/smithy-client" "^3.0.1" + "@smithy/types" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + tslib "^2.6.2" + +"@smithy/credential-provider-imds@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.0.0.tgz#a290eb0224ef045742e5c806685cf63d44a084f3" + integrity sha512-lfmBiFQcA3FsDAPxNfY0L7CawcWtbyWsBOHo34nF095728JLkBX4Y9q/VPPE2r7fqMVK+drmDigqE2/SSQeVRA== + dependencies: + "@smithy/node-config-provider" "^3.0.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-codec@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-3.0.0.tgz#81d30391220f73d41f432f65384b606d67673e46" + integrity sha512-PUtyEA0Oik50SaEFCZ0WPVtF9tz/teze2fDptW6WRXl+RrEenH8UbEjudOz8iakiMl3lE3lCVqYf2Y+znL8QFQ== + dependencies: + "@aws-crypto/crc32" "3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-browser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.0.tgz#94721b01f01d8b7eb1db5814275a774ed4d38190" + integrity sha512-NB7AFiPN4NxP/YCAnrvYR18z2/ZsiHiF7VtG30gshO9GbFrIb1rC8ep4NGpJSWrz6P64uhPXeo4M0UsCLnZKqw== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-config-resolver@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.0.tgz#420447d1d284d41f7f070a5d92fc3686cc922581" + integrity sha512-RUQG3vQ3LX7peqqHAbmayhgrF5aTilPnazinaSGF1P0+tgM3vvIRWPHmlLIz2qFqB9LqFIxditxc8O2Z6psrRw== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-node@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.0.tgz#6519523fbb429307be29b151b8ba35bcca2b6e64" + integrity sha512-baRPdMBDMBExZXIUAoPGm/hntixjt/VFpU6+VmCyiYJYzRHRxoaI1MN+5XE+hIS8AJ2GCHLMFEIOLzq9xx1EgQ== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-universal@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.0.tgz#cb8441a73fbde4cbaa68e4a21236f658d914a073" + integrity sha512-HNFfShmotWGeAoW4ujP8meV9BZavcpmerDbPIjkJbxKbN8RsUcpRQ/2OyIxWNxXNH2GWCAxuSB7ynmIGJlQ3Dw== + dependencies: + "@smithy/eventstream-codec" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/fetch-http-handler@^3.0.0", "@smithy/fetch-http-handler@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.0.1.tgz#dacfdf6e70d639fac4a0f57c42ce13f0ed14ff22" + integrity sha512-uaH74i5BDj+rBwoQaXioKpI0SHBJFtOVwzrCpxZxphOW0ki5jhj7dXvDMYM2IJem8TpdFvS2iC08sjOblfFGFg== + dependencies: + "@smithy/protocol-http" "^4.0.0" + "@smithy/querystring-builder" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-blob-browser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-3.0.0.tgz#63ef4c98f74c53cbcad8ec73387c68ec4708f55b" + integrity sha512-/Wbpdg+bwJvW7lxR/zpWAc1/x/YkcqguuF2bAzkJrvXriZu1vm8r+PUdE4syiVwQg7PPR2dXpi3CLBb9qRDaVQ== + dependencies: + "@smithy/chunked-blob-reader" "^3.0.0" + "@smithy/chunked-blob-reader-native" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-node@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.0.tgz#f44b5fff193e241c1cdcc957b296b60f186f0e59" + integrity sha512-84qXstNemP3XS5jcof0el6+bDfjzuvhJPQTEfro3lgtbCtKgzPm3MgiS6ehXVPjeQ5+JS0HqmTz8f/RYfzHVxw== + dependencies: + "@smithy/types" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-stream-node@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-3.0.0.tgz#b395a8a0d2427e4a8effc56135b37cb299339f8f" + integrity sha512-J0i7de+EgXDEGITD4fxzmMX8CyCNETTIRXlxjMiNUvvu76Xn3GJ31wQR85ynlPk2wI1lqoknAFJaD1fiNDlbIA== + dependencies: + "@smithy/types" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/invalid-dependency@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.0.tgz#21cb6b5203ee15321bfcc751f21f7a19536d4ae8" + integrity sha512-F6wBBaEFgJzj0s4KUlliIGPmqXemwP6EavgvDqYwCH40O5Xr2iMHvS8todmGVZtuJCorBkXsYLyTu4PuizVq5g== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz#9a95c2d46b8768946a9eec7f935feaddcffa5e7a" + integrity sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ== + dependencies: + tslib "^2.6.2" + +"@smithy/md5-js@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-3.0.0.tgz#6a2d1c496f4d4476a0fc84f7724d79b234c3eb13" + integrity sha512-Tm0vrrVzjlD+6RCQTx7D3Ls58S3FUH1ZCtU1MIh/qQmaOo1H9lMN2as6CikcEwgattnA9SURSdoJJ27xMcEfMA== + dependencies: + "@smithy/types" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/middleware-content-length@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.0.tgz#084b3d22248967885d496eb0b105d9090e8ababd" + integrity sha512-3C4s4d/iGobgCtk2tnWW6+zSTOBg1PRAm2vtWZLdriwTroFbbWNSr3lcyzHdrQHnEXYCC5K52EbpfodaIUY8sg== + dependencies: + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/middleware-endpoint@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.0.tgz#54c9e1bd8f35b7d004c803eaf3702e61e32b8295" + integrity sha512-aXOAWztw/5qAfp0NcA2OWpv6ZI/E+Dh9mByif7i91D/0iyYNUcKvskmXiowKESFkuZ7PIMd3VOR4fTibZDs2OQ== + dependencies: + "@smithy/middleware-serde" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/url-parser" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + tslib "^2.6.2" + +"@smithy/middleware-retry@^3.0.0", "@smithy/middleware-retry@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.1.tgz#167b75e9b79395f11a799f22030eaaf7d40da410" + integrity sha512-hBhSEuL841FhJBK/19WpaGk5YWSzFk/P2UaVjANGKRv3eYNO8Y1lANWgqnuPWjOyCEWMPr58vELFDWpxvRKANw== + dependencies: + "@smithy/node-config-provider" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/service-error-classification" "^3.0.0" + "@smithy/smithy-client" "^3.0.1" + "@smithy/types" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-retry" "^3.0.0" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/middleware-serde@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.0.tgz#786da6a6bc0e5e51d669dac834c19965245dd302" + integrity sha512-I1vKG1foI+oPgG9r7IMY1S+xBnmAn1ISqployvqkwHoSb8VPsngHDTOgYGYBonuOKndaWRUGJZrKYYLB+Ane6w== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/middleware-stack@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.0.tgz#00f112bae7af5fc3bd37d4fab95ebce0f17a7774" + integrity sha512-+H0jmyfAyHRFXm6wunskuNAqtj7yfmwFB6Fp37enytp2q047/Od9xetEaUbluyImOlGnGpaVGaVfjwawSr+i6Q== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/node-config-provider@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.0.0.tgz#4cd5dcf6132c75d6a582fcd6243482dac703865a" + integrity sha512-buqfaSdDh0zo62EPLf8rGDvcpKwGpO5ho4bXS2cdFhlOta7tBkWJt+O5uiaAeICfIOfPclNOndshDNSanX2X9g== + dependencies: + "@smithy/property-provider" "^3.0.0" + "@smithy/shared-ini-file-loader" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/node-http-handler@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.0.0.tgz#e771ea95d03e259f04b7b37e8aece8a4fffc8cdc" + integrity sha512-3trD4r7NOMygwLbUJo4eodyQuypAWr7uvPnebNJ9a70dQhVn+US8j/lCnvoJS6BXfZeF7PkkkI0DemVJw+n+eQ== + dependencies: + "@smithy/abort-controller" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/querystring-builder" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/property-provider@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.0.0.tgz#ef7a26557c855cc1471b9aa0e05529183e99b978" + integrity sha512-LmbPgHBswdXCrkWWuUwBm9w72S2iLWyC/5jet9/Y9cGHtzqxi+GVjfCfahkvNV4KXEwgnH8EMpcrD9RUYe0eLQ== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/protocol-http@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.0.0.tgz#04df3b5674b540323f678e7c4113e8abd8b26432" + integrity sha512-qOQZOEI2XLWRWBO9AgIYuHuqjZ2csyr8/IlgFDHDNuIgLAMRx2Bl8ck5U5D6Vh9DPdoaVpuzwWMa0xcdL4O/AQ== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/querystring-builder@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.0.tgz#48a9aa7b700e8409368c21bc0adf7564e001daea" + integrity sha512-bW8Fi0NzyfkE0TmQphDXr1AmBDbK01cA4C1Z7ggwMAU5RDz5AAv/KmoRwzQAS0kxXNf/D2ALTEgwK0U2c4LtRg== + dependencies: + "@smithy/types" "^3.0.0" + "@smithy/util-uri-escape" "^3.0.0" + tslib "^2.6.2" + +"@smithy/querystring-parser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.0.tgz#fa1ed0cee408cd4d622070fa874bc50ac1a379b7" + integrity sha512-UzHwthk0UEccV4dHzPySnBy34AWw3V9lIqUTxmozQ+wPDAO9csCWMfOLe7V9A2agNYy7xE+Pb0S6K/J23JSzfQ== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" -"@sentry/cli-darwin@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.28.6.tgz#83f9127de77e2a2d25eb143d90720b3e9042adc1" - integrity sha512-KRf0VvTltHQ5gA7CdbUkaIp222LAk/f1+KqpDzO6nB/jC/tL4sfiy6YyM4uiH6IbVEudB8WpHCECiatmyAqMBA== +"@smithy/service-error-classification@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.0.tgz#06a45cb91b15b8b0d5f3b1df2b3743d2ca42f5c4" + integrity sha512-3BsBtOUt2Gsnc3X23ew+r2M71WwtpHfEDGhHYHSDg6q1t8FrWh15jT25DLajFV1H+PpxAJ6gqe9yYeRUsmSdFA== + dependencies: + "@smithy/types" "^3.0.0" -"@sentry/cli-linux-arm64@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.28.6.tgz#6bb660e5d8145270e287a9a21201d2f9576b0634" - integrity sha512-caMDt37FI752n4/3pVltDjlrRlPFCOxK4PHvoZGQ3KFMsai0ZhE/0CLBUMQqfZf0M0r8KB2x7wqLm7xSELjefQ== +"@smithy/shared-ini-file-loader@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.0.0.tgz#8739b7cd24f55fb4e276a74f00f0c2bb4e3f25d8" + integrity sha512-REVw6XauXk8xE4zo5aGL7Rz4ywA8qNMUn8RtWeTRQsgAlmlvbJ7CEPBcaXU2NDC3AYBgYAXrGyWD8XrN8UGDog== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" -"@sentry/cli-linux-arm@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.28.6.tgz#73d466004ac445d9258e83a7b3d4e0ee6604e0bd" - integrity sha512-ANG7U47yEHD1g3JrfhpT4/MclEvmDZhctWgSP5gVw5X4AlcI87E6dTqccnLgvZjiIAQTaJJAZuSHVVF3Jk403w== +"@smithy/signature-v4@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-3.0.0.tgz#f536d0abebfeeca8e9aab846a4042658ca07d3b7" + integrity sha512-kXFOkNX+BQHe2qnLxpMEaCRGap9J6tUGLzc3A9jdn+nD4JdMwCKTJ+zFwQ20GkY+mAXGatyTw3HcoUlR39HwmA== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-middleware" "^3.0.0" + "@smithy/util-uri-escape" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" -"@sentry/cli-linux-i686@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.28.6.tgz#f7175ca639ee05cf12d808f7fc31d59d6e2ee3b9" - integrity sha512-Tj1+GMc6lFsDRquOqaGKXFpW9QbmNK4TSfynkWKiJxdTEn5jSMlXXfr0r9OQrxu3dCCqEHkhEyU63NYVpgxIPw== +"@smithy/smithy-client@^3.0.0", "@smithy/smithy-client@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.0.1.tgz#c440473f6fb5dfbe86eaf015565fc56f66533bb4" + integrity sha512-KAiFY4Y4jdHxR+4zerH/VBhaFKM8pbaVmJZ/CWJRwtM/CmwzTfXfvYwf6GoUwiHepdv+lwiOXCuOl6UBDUEINw== + dependencies: + "@smithy/middleware-endpoint" "^3.0.0" + "@smithy/middleware-stack" "^3.0.0" + "@smithy/protocol-http" "^4.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-stream" "^3.0.1" + tslib "^2.6.2" -"@sentry/cli-linux-x64@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.28.6.tgz#df0af8d6c8c8c880eb7345c715a4dfa509544a40" - integrity sha512-Dt/Xz784w/z3tEObfyJEMmRIzn0D5qoK53H9kZ6e0yNvJOSKNCSOq5cQk4n1/qeG0K/6SU9dirmvHwFUiVNyYg== +"@smithy/types@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.0.0.tgz#00231052945159c64ffd8b91e8909d8d3006cb7e" + integrity sha512-VvWuQk2RKFuOr98gFhjca7fkBS+xLLURT8bUjk5XQoV0ZLm7WPwWPPY3/AwzTLuUBDeoKDCthfe1AsTUWaSEhw== + dependencies: + tslib "^2.6.2" -"@sentry/cli-win32-i686@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.28.6.tgz#0df19912d1823b6ec034b4c4c714c7601211c926" - integrity sha512-zkpWtvY3kt+ogVaAbfFr2MEkgMMHJNJUnNMO8Ixce9gh38sybIkDkZNFnVPBXMClJV0APa4QH0EwumYBFZUMuQ== +"@smithy/url-parser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.0.tgz#5fdc77cd22051c1aac6531be0315bfcba0fa705d" + integrity sha512-2XLazFgUu+YOGHtWihB3FSLAfCUajVfNBXGGYjOaVKjLAuAxx3pSBY3hBgLzIgB17haf59gOG3imKqTy8mcrjw== + dependencies: + "@smithy/querystring-parser" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" -"@sentry/cli-win32-x64@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.28.6.tgz#2344a206be3b555ec6540740f93a181199962804" - integrity sha512-TG2YzZ9JMeNFzbicdr5fbtsusVGACbrEfHmPgzWGDeLUP90mZxiMTjkXsE1X/5jQEQjB2+fyfXloba/Ugo51hA== +"@smithy/util-base64@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-3.0.0.tgz#f7a9a82adf34e27a72d0719395713edf0e493017" + integrity sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ== + dependencies: + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" -"@sentry/cli@^2.22.3": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.28.6.tgz#645f31b9e742e7bf7668c8f867149359e79b8123" - integrity sha512-o2Ngz7xXuhwHxMi+4BFgZ4qjkX0tdZeOSIZkFAGnTbRhQe5T8bxq6CcQRLdPhqMgqvDn7XuJ3YlFtD3ZjHvD7g== +"@smithy/util-body-length-browser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz#86ec2f6256310b4845a2f064e2f571c1ca164ded" + integrity sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ== dependencies: - https-proxy-agent "^5.0.0" - node-fetch "^2.6.7" - progress "^2.0.3" - proxy-from-env "^1.1.0" - which "^2.0.2" - optionalDependencies: - "@sentry/cli-darwin" "2.28.6" - "@sentry/cli-linux-arm" "2.28.6" - "@sentry/cli-linux-arm64" "2.28.6" - "@sentry/cli-linux-i686" "2.28.6" - "@sentry/cli-linux-x64" "2.28.6" - "@sentry/cli-win32-i686" "2.28.6" - "@sentry/cli-win32-x64" "2.28.6" + tslib "^2.6.2" -"@sentry/core@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.102.1.tgz#855d37b6bba9986a9380864c823e696d3fc5aa01" - integrity sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg== +"@smithy/util-body-length-node@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz#99a291bae40d8932166907fe981d6a1f54298a6d" + integrity sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA== dependencies: - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + tslib "^2.6.2" -"@sentry/replay@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.102.1.tgz#d6c17332d14dc312b124bbbda8f35d6a982b893c" - integrity sha512-HR/j9dGIvbrId8fh8mQlODx7JrhRmawEd9e9P3laPtogWCg/5TI+XPb2VGSaXOX9VWtb/6Z2UjHsaGjgg6YcuA== +"@smithy/util-buffer-from@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz#559fc1c86138a89b2edaefc1e6677780c24594e3" + integrity sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA== dependencies: - "@sentry-internal/tracing" "7.102.1" - "@sentry/core" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@smithy/is-array-buffer" "^3.0.0" + tslib "^2.6.2" -"@sentry/types@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.102.1.tgz#18c35f32ecbd12afb9860ca2de7bfff542d10b27" - integrity sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA== +"@smithy/util-config-provider@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz#62c6b73b22a430e84888a8f8da4b6029dd5b8efe" + integrity sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ== + dependencies: + tslib "^2.6.2" -"@sentry/utils@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.102.1.tgz#45ddcdf2e700d40160347bbdf4233aff3179d398" - integrity sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg== +"@smithy/util-defaults-mode-browser@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.1.tgz#0ba33ec90f6dd311599bed3a3dd604f3adba9acd" + integrity sha512-nW5kEzdJn1Bn5TF+gOPHh2rcPli8JU9vSSXLbfg7uPnfR1TMRQqs9zlYRhIb87NeSxIbpdXOI94tvXSy+fvDYg== dependencies: - "@sentry/types" "7.102.1" + "@smithy/property-provider" "^3.0.0" + "@smithy/smithy-client" "^3.0.1" + "@smithy/types" "^3.0.0" + bowser "^2.11.0" + tslib "^2.6.2" -"@sentry/vite-plugin@^2.7.1": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.14.2.tgz#f17cbd5526a95de3d8a7995b4cd90e2c8a548bf1" - integrity sha512-t8IiRZGxivtODgabjgHlgUhOBEIJdOclJGUKLAJjJqPtYeKjPzxYOo/Z5yt7k1rhBAaMhFk3whW5o7SOq4KVOA== +"@smithy/util-defaults-mode-node@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.1.tgz#71242a6978240a6f559445d4cc26f2cce91c90e1" + integrity sha512-TFk+Qb+elLc/MOhtSp+50fstyfZ6avQbgH2d96xUBpeScu+Al9elxv+UFAjaTHe0HQe5n+wem8ZLpXvU8lwV6Q== + dependencies: + "@smithy/config-resolver" "^3.0.0" + "@smithy/credential-provider-imds" "^3.0.0" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/property-provider" "^3.0.0" + "@smithy/smithy-client" "^3.0.1" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-endpoints@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.0.0.tgz#5a16a723c1220f536a9b1b3e01787e69e77b6f12" + integrity sha512-+exaXzEY3DNt2qtA2OtRNSDlVrE4p32j1JSsQkzA5AdP0YtJNjkYbYhJxkFmPYcjI1abuwopOZCwUmv682QkiQ== dependencies: - "@sentry/bundler-plugin-core" "2.14.2" - unplugin "1.0.1" + "@smithy/node-config-provider" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" -"@sentry/vue@^7.71.0": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/vue/-/vue-7.102.1.tgz#3dea7987dae7338a428a525f94b44e29d90ff6b1" - integrity sha512-7sTrdAe3EL45MaA44mAgSPRg7jQ/CE6LifHl+62hjchpzsh+W+xWsN+31hbvm9ek6v/gNnQAlxyAXqXBRWtrlQ== +"@smithy/util-hex-encoding@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz#32938b33d5bf2a15796cd3f178a55b4155c535e6" + integrity sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ== dependencies: - "@sentry/browser" "7.102.1" - "@sentry/core" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + tslib "^2.6.2" -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@smithy/util-middleware@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.0.tgz#64d775628b99a495ca83ce982f5c83aa45f1e894" + integrity sha512-q5ITdOnV2pXHSVDnKWrwgSNTDBAMHLptFE07ua/5Ty5WJ11bvr0vk2a7agu7qRhrCFRQlno5u3CneU5EELK+DQ== + dependencies: + "@smithy/types" "^3.0.0" + tslib "^2.6.2" -"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@smithy/util-retry@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.0.tgz#8a0c47496aab74e1dfde4905d462ad636a8824bb" + integrity sha512-nK99bvJiziGv/UOKJlDvFF45F00WgPLKVIGUfAK+mDhzVN2hb/S33uW2Tlhg5PVBoqY7tDVqL0zmu4OxAHgo9g== + dependencies: + "@smithy/service-error-classification" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" -"@sinonjs/commons@^3.0.0": +"@smithy/util-stream@^3.0.0", "@smithy/util-stream@^3.0.1": version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.0.1.tgz#3cf527bcd3fec82c231c38d47dd75f3364747edb" + integrity sha512-7F7VNNhAsfMRA8I986YdOY5fE0/T1/ZjFF6OLsqkvQVNP3vZ/szYDfGCyphb7ioA09r32K/0qbSFfNFU68aSzA== + dependencies: + "@smithy/fetch-http-handler" "^3.0.1" + "@smithy/node-http-handler" "^3.0.0" + "@smithy/types" "^3.0.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-uri-escape@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz#e43358a78bf45d50bb736770077f0f09195b6f54" + integrity sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg== dependencies: - type-detect "4.0.8" + tslib "^2.6.2" -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== +"@smithy/util-utf8@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-3.0.0.tgz#1a6a823d47cbec1fd6933e5fc87df975286d9d6a" + integrity sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA== dependencies: - "@sinonjs/commons" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-waiter@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-3.0.0.tgz#26bcc5bbbf1de9360a7aeb3b3919926fc6afa2bc" + integrity sha512-+fEXJxGDLCoqRKVSmo0auGxaqbiCo+8oph+4auefYjaNxjOLKSY2MxVQfRzo65PaZv4fr+5lWg+au7vSuJJ/zw== + dependencies: + "@smithy/abort-controller" "^3.0.0" + "@smithy/types" "^3.0.0" + tslib "^2.6.2" "@socket.io/component-emitter@~3.1.0": version "3.1.0" @@ -3764,6 +6824,18 @@ resolved "https://registry.yarnpkg.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz#a53515db25d8038374381b73af20bb4f2e508d87" integrity sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w== +"@solidity-parser/parser@^0.14.0": + version "0.14.5" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" + integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@solidity-parser/parser@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" + integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== + "@stablelib/aead@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" @@ -3898,6 +6970,112 @@ "@stablelib/random" "^1.0.2" "@stablelib/wipe" "^1.0.1" +"@svgr/babel-plugin-add-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" + integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== + +"@svgr/babel-plugin-remove-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" + integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== + +"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" + integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" + integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== + +"@svgr/babel-plugin-svg-dynamic-title@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" + integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== + +"@svgr/babel-plugin-svg-em-dimensions@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" + integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== + +"@svgr/babel-plugin-transform-react-native-svg@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754" + integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q== + +"@svgr/babel-plugin-transform-svg-component@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" + integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== + +"@svgr/babel-preset@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece" + integrity sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "8.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "8.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "8.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "8.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "8.1.0" + "@svgr/babel-plugin-transform-svg-component" "8.0.0" + +"@svgr/core@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88" + integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.1.0" + camelcase "^6.2.0" + cosmiconfig "^8.1.3" + snake-case "^3.0.4" + +"@svgr/hast-util-to-babel-ast@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" + integrity sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q== + dependencies: + "@babel/types" "^7.21.3" + entities "^4.4.0" + +"@svgr/plugin-jsx@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928" + integrity sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.1.0" + "@svgr/hast-util-to-babel-ast" "8.0.0" + svg-parser "^2.0.4" + +"@svgr/plugin-svgo@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz#b115b7b967b564f89ac58feae89b88c3decd0f00" + integrity sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA== + dependencies: + cosmiconfig "^8.1.3" + deepmerge "^4.3.1" + svgo "^3.0.2" + +"@svgr/webpack@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.1.0.tgz#16f1b5346f102f89fda6ec7338b96a701d8be0c2" + integrity sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA== + dependencies: + "@babel/core" "^7.21.3" + "@babel/plugin-transform-react-constant-elements" "^7.21.3" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.21.0" + "@svgr/core" "8.1.0" + "@svgr/plugin-jsx" "8.1.0" + "@svgr/plugin-svgo" "8.1.0" + "@swc-node/core@^1.12.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@swc-node/core/-/core-1.13.0.tgz#209d70f6371049926915a7d23a502144cdb5cffb" @@ -4023,11 +7201,6 @@ dependencies: defer-to-connect "^2.0.1" -"@thxnetwork/sdk@1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@thxnetwork/sdk/-/sdk-1.3.5.tgz#ef20dc0151b7a7a0bf5a02bee7ca4bc1cbbd5216" - integrity sha512-sZFXgOgd5xesygf3iGpZyzhRghrzTwkCwDEfdkqMgNHZCc6dFJAaPxzCxstWUyiilqgkQwMFk1AScBbdXP3eew== - "@tkey/common-types@^10.1.0": version "10.1.0" resolved "https://registry.yarnpkg.com/@tkey/common-types/-/common-types-10.1.0.tgz#1cde24b6e0d614e2c5eaa5eeeadc2607d2b0c714" @@ -4238,6 +7411,11 @@ json-stable-stringify "^1.0.2" loglevel "^1.8.1" +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -4258,6 +7436,28 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@typechain/ethers-v5@^10.1.0": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz#50241e6957683281ecfa03fb5a6724d8a3ce2391" + integrity sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A== + dependencies: + lodash "^4.17.15" + ts-essentials "^7.0.1" + +"@typechain/hardhat@^6.1.2": + version "6.1.6" + resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.6.tgz#1a749eb35e5054c80df531cf440819cb347c62ea" + integrity sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA== + dependencies: + fs-extra "^9.1.0" + +"@types/accepts@*": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" + integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ== + dependencies: + "@types/node" "*" + "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -4291,6 +7491,13 @@ dependencies: "@babel/types" "^7.20.7" +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + "@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": version "5.1.5" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" @@ -4306,6 +7513,13 @@ "@types/connect" "*" "@types/node" "*" +"@types/bonjour@^3.5.9": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + "@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": version "6.0.3" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" @@ -4316,6 +7530,18 @@ "@types/node" "*" "@types/responselike" "^1.0.0" +"@types/chai-as-promised@^7.1.3": + version "7.1.8" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz#f2b3d82d53c59626b5d6bbc087667ccb4b677fe9" + integrity sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.2.0": + version "4.3.16" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" + integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ== + "@types/chrome@^0.0.136": version "0.0.136" resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.136.tgz#7c011b9f997b0156f25a140188a0c5689d3f368f" @@ -4343,7 +7569,21 @@ dependencies: "@types/color-convert" "*" -"@types/connect-history-api-fallback@*": +"@types/compression@^1.7.5": + version "1.7.5" + resolved "https://registry.yarnpkg.com/@types/compression/-/compression-1.7.5.tgz#0f80efef6eb031be57b12221c4ba6bc3577808f7" + integrity sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg== + dependencies: + "@types/express" "*" + +"@types/concat-stream@^1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74" + integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@*", "@types/connect-history-api-fallback@^1.3.5": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== @@ -4358,6 +7598,26 @@ dependencies: "@types/node" "*" +"@types/content-disposition@*": + version "0.5.8" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.8.tgz#6742a5971f490dc41e59d277eee71361fea0b537" + integrity sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg== + +"@types/cookiejar@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" + integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== + +"@types/cookies@*": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.9.0.tgz#a2290cfb325f75f0f28720939bee854d4142aee2" + integrity sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + "@types/debug@^4.1.7": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" @@ -4370,6 +7630,27 @@ resolved "https://registry.yarnpkg.com/@types/dom-screen-wake-lock/-/dom-screen-wake-lock-1.0.3.tgz#c3588a5f6f40fae957f9ce5be9bc4927a61bb9a0" integrity sha512-3Iten7X3Zgwvk6kh6/NRdwN7WbZ760YgFCsF5AxDifltUQzW1RaW+WRmcVtgwFzLjaNu64H+0MPJ13yRa8g3Dw== +"@types/ejs@^3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.5.tgz#49d738257cc73bafe45c13cb8ff240683b4d5117" + integrity sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg== + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" + integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + "@types/eslint@^8.4.5": version "8.56.3" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.3.tgz#d1f6b2303ac5ed53cb2cf59e0ab680cde1698f5f" @@ -4378,7 +7659,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0", "@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -4393,7 +7674,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@*": +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.17", "@types/express@~4.17.13": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -4415,6 +7696,13 @@ resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.33.tgz#d9d611db9d9cd99ae4e458de420eeb64ad604ea8" integrity sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g== +"@types/form-data@0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw== + dependencies: + "@types/node" "*" + "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" @@ -4435,6 +7723,11 @@ resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.15.tgz#f352493638c2f89d706438a19a9eb300b493b506" integrity sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA== +"@types/http-assert@*": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.5.tgz#dfb1063eb7c240ee3d3fe213dac5671cfb6a8dbf" + integrity sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g== + "@types/http-cache-semantics@*": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" @@ -4445,7 +7738,7 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== -"@types/http-proxy@^1.17.5": +"@types/http-proxy@^1.17.5", "@types/http-proxy@^1.17.8": version "1.17.14" resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== @@ -4488,7 +7781,7 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5": +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -4498,6 +7791,18 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/jsonwebtoken@^9", "@types/jsonwebtoken@^9.0.2": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz#d1af3544d99ad992fb6681bbe60676e06b032bd3" + integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw== + dependencies: + "@types/node" "*" + +"@types/keygrip@*": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.6.tgz#1749535181a2a9b02ac04a797550a8787345b740" + integrity sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ== + "@types/keyv@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" @@ -4505,6 +7810,52 @@ dependencies: "@types/node" "*" +"@types/koa-compose@*": + version "3.2.8" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.8.tgz#dec48de1f6b3d87f87320097686a915f1e954b57" + integrity sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA== + dependencies: + "@types/koa" "*" + +"@types/koa@*": + version "2.15.0" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.15.0.tgz#eca43d76f527c803b491731f95df575636e7b6f2" + integrity sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/lru-cache@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + +"@types/lusca@^1.7.5": + version "1.7.5" + resolved "https://registry.yarnpkg.com/@types/lusca/-/lusca-1.7.5.tgz#6fff257dc11176bd3150afba90790e626a12cd0f" + integrity sha512-l49gAf8pu2iMzbKejLcz6Pqj+51H2na6BgORv1ElnE8ByPFcBdh/eZ0WNR1Va/6ZuNSZa01Hoy1DTZ3IZ+y+kA== + dependencies: + "@types/express" "*" + +"@types/methods@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" + integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== + +"@types/migrate-mongo@^10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@types/migrate-mongo/-/migrate-mongo-10.0.4.tgz#5b68fb9c3ca516e4f025ebca34021880137f94a4" + integrity sha512-+9JAzkIbxgox33wCT18bdpZ6ASYo1qwbf4Gg8kTbqvT48ajosSbZBGg94Vsy3baFzT6+DUXWylY/DcoOWOPNRg== + dependencies: + "@types/node" "*" + mongodb "^6.1.0" + "@types/mime@*": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" @@ -4530,11 +7881,38 @@ resolved "https://registry.yarnpkg.com/@types/mixpanel-browser/-/mixpanel-browser-2.49.0.tgz#ad92ecc36fad63b9c0aed80b6283d86dbf52e49e" integrity sha512-StmgUnS58d44DmIAEX9Kk8qwisAYCl6E2qulIjYyHXUPuJCPOuyUMTTKBp+aU2F2do+kxAzCxiBtsB4fnBT9Fg== +"@types/mocha@>=9.1.0": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.6.tgz#818551d39113081048bdddbef96701b4e8bb9d1b" + integrity sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg== + "@types/ms@*": version "0.7.34" resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== +"@types/multer@^1.4.11": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.11.tgz#c70792670513b4af1159a2b60bf48cc932af55c5" + integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== + dependencies: + "@types/express" "*" + +"@types/node-fetch@^2.5.7": + version "2.6.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + "@types/node@*": version "20.11.20" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.20.tgz#f0a2aee575215149a62784210ad88b3a34843659" @@ -4542,10 +7920,12 @@ dependencies: undici-types "~5.26.4" -"@types/node@18.16.9": - version "18.16.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.9.tgz#e79416d778a8714597342bb87efb5a6e914f7a73" - integrity sha512-IeB32oIV4oGArLrd7znD2rkHQ6EDCM+2Sr76dJnrHwv9OHBTTM6nuDLK9bmikXzPa0ZlWMWtRGo/Uw4mrzQedA== +"@types/node@>=12.12.47": + version "20.12.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050" + integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw== + dependencies: + undici-types "~5.26.4" "@types/node@>=13.7.0": version "20.11.25" @@ -4554,16 +7934,38 @@ dependencies: undici-types "~5.26.4" +"@types/node@^10.0.3": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + "@types/node@^12.12.6": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== +"@types/node@^8.0.0": + version "8.10.66" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" + integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== + +"@types/node@~18.16.9": + version "18.16.20" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.20.tgz#b27be1ceb267bfb47d8bac024ff6379998f62207" + integrity sha512-nL54VfDjThdP2UXJXZao5wp76CDiDw4zSRO8d4Tk7UgDqNKGKVEQB0/t3ti63NS+YNNkIQDvwEAF04BO+WYu7Q== + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== +"@types/oidc-provider@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@types/oidc-provider/-/oidc-provider-7.14.0.tgz#5ca627e0b748f2a1a78a2aabbba7d57ce4c46f8e" + integrity sha512-zIoedB25LuuiNb0tqRQYI3BzdHXVCsZrCHm38apiLe1p6TmbZA7dCSv8rH3AR8xyBk7eNiE+iIBDEHlBx4UzPA== + dependencies: + "@types/koa" "*" + "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" @@ -4576,6 +7978,11 @@ dependencies: "@types/node" "*" +"@types/prettier@^2.1.1": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + "@types/q@^1.5.1": version "1.5.8" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837" @@ -4586,11 +7993,24 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== +"@types/qs@^6.2.31": + version "6.9.15" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + "@types/range-parser@*": version "1.2.7" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== +"@types/readable-stream@^2.3.13": + version "2.3.15" + resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" + integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== + dependencies: + "@types/node" "*" + safe-buffer "~5.1.1" + "@types/responselike@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" @@ -4598,6 +8018,11 @@ dependencies: "@types/node" "*" +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + "@types/secp256k1@^4.0.1", "@types/secp256k1@^4.0.4": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" @@ -4605,11 +8030,6 @@ dependencies: "@types/node" "*" -"@types/semver@^7.5.0": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - "@types/send@*": version "0.17.4" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" @@ -4618,6 +8038,13 @@ "@types/mime" "^1" "@types/node" "*" +"@types/serve-index@^1.9.1": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + "@types/serve-static@*": version "1.15.5" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" @@ -4627,6 +8054,15 @@ "@types/mime" "*" "@types/node" "*" +"@types/serve-static@^1.13.10": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + "@types/sinonjs__fake-timers@8.1.1": version "8.1.1" resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" @@ -4637,6 +8073,13 @@ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627" integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg== +"@types/sockjs@^0.3.33": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + "@types/source-list-map@*": version "0.1.6" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.6.tgz#164e169dd061795b50b83c19e4d3be09f8d3a454" @@ -4657,6 +8100,23 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/superagent@^8.1.0": + version "8.1.7" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.7.tgz#1153819ed4db34427409a1cc58f3e2f13eeec862" + integrity sha512-NmIsd0Yj4DDhftfWvvAku482PZum4DBW7U51OvS8gvOkDDY0WT1jsVyDV3hK+vplrsYw8oDwi9QxOM7U68iwww== + dependencies: + "@types/cookiejar" "^2.1.5" + "@types/methods" "^1.1.4" + "@types/node" "*" + +"@types/supertest@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-6.0.2.tgz#2af1c466456aaf82c7c6106c6b5cbd73a5e86588" + integrity sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg== + dependencies: + "@types/methods" "^1.1.4" + "@types/superagent" "^8.1.0" + "@types/tapable@^1": version "1.0.12" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.12.tgz#bc2cab12e87978eee89fb21576b670350d6d86ab" @@ -4667,6 +8127,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + "@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" @@ -4684,11 +8149,21 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== +"@types/validator@^13.11.8": + version "13.11.10" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.10.tgz#feb364018cdd1f3d970a9e8c7f1c314c0a264fff" + integrity sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg== + "@types/web-bluetooth@^0.0.20": version "0.0.20" resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597" integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow== +"@types/webidl-conversions@*": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859" + integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA== + "@types/webpack-dev-server@^3.11.0": version "3.11.6" resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz#d8888cfd2f0630203e13d3ed7833a4d11b8a34dc" @@ -4726,6 +8201,28 @@ anymatch "^3.0.0" source-map "^0.6.0" +"@types/whatwg-url@^11.0.2": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.4.tgz#ffed0dc8d89d91f62e3f368fcbda222a487c4f63" + integrity sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw== + dependencies: + "@types/webidl-conversions" "*" + +"@types/whatwg-url@^8.2.1": + version "8.2.2" + resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63" + integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA== + dependencies: + "@types/node" "*" + "@types/webidl-conversions" "*" + +"@types/ws@^8.5.10", "@types/ws@^8.5.5": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -4745,91 +8242,91 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" - integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== +"@typescript-eslint/eslint-plugin@7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz#093b96fc4e342226e65d5f18f9c87081e0b04a31" + integrity sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA== dependencies: - "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/type-utils" "6.21.0" - "@typescript-eslint/utils" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.9.0" + "@typescript-eslint/type-utils" "7.9.0" + "@typescript-eslint/utils" "7.9.0" + "@typescript-eslint/visitor-keys" "7.9.0" graphemer "^1.4.0" - ignore "^5.2.4" + ignore "^5.3.1" natural-compare "^1.4.0" - semver "^7.5.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" -"@typescript-eslint/parser@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" - integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== +"@typescript-eslint/parser@7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.9.0.tgz#fb3ba01b75e0e65cb78037a360961b00301f6c70" + integrity sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ== dependencies: - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/scope-manager" "7.9.0" + "@typescript-eslint/types" "7.9.0" + "@typescript-eslint/typescript-estree" "7.9.0" + "@typescript-eslint/visitor-keys" "7.9.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" - integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== +"@typescript-eslint/scope-manager@7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz#1dd3e63a4411db356a9d040e75864851b5f2619b" + integrity sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ== dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/types" "7.9.0" + "@typescript-eslint/visitor-keys" "7.9.0" -"@typescript-eslint/type-utils@6.21.0", "@typescript-eslint/type-utils@^6.13.2": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" - integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== +"@typescript-eslint/type-utils@7.9.0", "@typescript-eslint/type-utils@^7.3.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz#f523262e1b66ca65540b7a65a1222db52e0a90c9" + integrity sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA== dependencies: - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/typescript-estree" "7.9.0" + "@typescript-eslint/utils" "7.9.0" debug "^4.3.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" -"@typescript-eslint/types@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" - integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== +"@typescript-eslint/types@7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.9.0.tgz#b58e485e4bfba055659c7e683ad4f5f0821ae2ec" + integrity sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w== -"@typescript-eslint/typescript-estree@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" - integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== +"@typescript-eslint/typescript-estree@7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz#3395e27656060dc313a6b406c3a298b729685e07" + integrity sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg== dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/types" "7.9.0" + "@typescript-eslint/visitor-keys" "7.9.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/utils@6.21.0", "@typescript-eslint/utils@^6.13.2": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" - integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== +"@typescript-eslint/utils@7.9.0", "@typescript-eslint/utils@^7.3.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.9.0.tgz#1b96a34eefdca1c820cb1bbc2751d848b4540899" + integrity sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - semver "^7.5.4" + "@typescript-eslint/scope-manager" "7.9.0" + "@typescript-eslint/types" "7.9.0" + "@typescript-eslint/typescript-estree" "7.9.0" -"@typescript-eslint/visitor-keys@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" - integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== +"@typescript-eslint/visitor-keys@7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz#82162656e339c3def02895f5c8546f6888d9b9ea" + integrity sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ== dependencies: - "@typescript-eslint/types" "6.21.0" - eslint-visitor-keys "^3.4.1" + "@typescript-eslint/types" "7.9.0" + eslint-visitor-keys "^3.4.3" + +"@tyriar/fibonacci-heap@^2.0.7": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@tyriar/fibonacci-heap/-/fibonacci-heap-2.0.9.tgz#df3dcbdb1b9182168601f6318366157ee16666e9" + integrity sha512-bYuSNomfn4hu2tPiDN+JZtnzCpSpbJ/PNeulmocDy3xN2X5OkJL65zo6rPZp65cPPhLF9vfT/dgE+RtFRCSxOA== "@ungap/structured-clone@^1.2.0": version "1.2.0" @@ -4841,10 +8338,10 @@ resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz#508d6a0f2440f86945835d903fcc0d95d1bb8a37" integrity sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ== -"@vitest/coverage-v8@^1.0.4": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz#78ba9e182ff4cd1eba79c45cfafd2edc4c2941ec" - integrity sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg== +"@vitest/coverage-v8@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz#2f54ccf4c2d9f23a71294aba7f95b3d2e27d14e7" + integrity sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew== dependencies: "@ampproject/remapping" "^2.2.1" "@bcoe/v8-coverage" "^0.2.3" @@ -4859,48 +8356,47 @@ std-env "^3.5.0" strip-literal "^2.0.0" test-exclude "^6.0.0" - v8-to-istanbul "^9.2.0" -"@vitest/expect@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.4.0.tgz#d64e17838a20007fecd252397f9b96a1ca81bfb0" - integrity sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA== +"@vitest/expect@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.6.0.tgz#0b3ba0914f738508464983f4d811bc122b51fb30" + integrity sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ== dependencies: - "@vitest/spy" "1.4.0" - "@vitest/utils" "1.4.0" + "@vitest/spy" "1.6.0" + "@vitest/utils" "1.6.0" chai "^4.3.10" -"@vitest/runner@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.4.0.tgz#907c2d17ad5975b70882c25ab7a13b73e5a28da9" - integrity sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg== +"@vitest/runner@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.6.0.tgz#a6de49a96cb33b0e3ba0d9064a3e8d6ce2f08825" + integrity sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg== dependencies: - "@vitest/utils" "1.4.0" + "@vitest/utils" "1.6.0" p-limit "^5.0.0" pathe "^1.1.1" -"@vitest/snapshot@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.4.0.tgz#2945b3fb53767a3f4f421919e93edfef2935b8bd" - integrity sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A== +"@vitest/snapshot@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.6.0.tgz#deb7e4498a5299c1198136f56e6e0f692e6af470" + integrity sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ== dependencies: magic-string "^0.30.5" pathe "^1.1.1" pretty-format "^29.7.0" -"@vitest/spy@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.4.0.tgz#cf953c93ae54885e801cbe6b408a547ae613f26c" - integrity sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q== +"@vitest/spy@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.6.0.tgz#362cbd42ccdb03f1613798fde99799649516906d" + integrity sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw== dependencies: tinyspy "^2.2.0" -"@vitest/ui@^1.3.1": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-1.4.0.tgz#01d34162bcee14c7e45cbf3e9dee4a22b52d778a" - integrity sha512-XC6CMhN1gzYcGbpn6/Oanj4Au2EXwQEX6vpcOeLlZv8dy7g11Ukx8zwtYQbwxs9duK2s9j2o5rbQiCP5DPAcmw== +"@vitest/ui@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-1.6.0.tgz#ffcc97ebcceca7fec840c29ab68632d0cd01db93" + integrity sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA== dependencies: - "@vitest/utils" "1.4.0" + "@vitest/utils" "1.6.0" fast-glob "^3.3.2" fflate "^0.8.1" flatted "^3.2.9" @@ -4908,16 +8404,21 @@ picocolors "^1.0.0" sirv "^2.0.4" -"@vitest/utils@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.4.0.tgz#ea6297e0d329f9ff0a106f4e1f6daf3ff6aad3f0" - integrity sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg== +"@vitest/utils@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.6.0.tgz#5c5675ca7d6f546a7b4337de9ae882e6c57896a1" + integrity sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw== dependencies: diff-sequences "^29.6.3" estree-walker "^3.0.3" loupe "^2.3.7" pretty-format "^29.7.0" +"@vladfrangu/async_event_emitter@^2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz#d3537432c6db6444680a596271dff8ea407343b3" + integrity sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug== + "@vue/babel-helper-vue-jsx-merge-props@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2" @@ -5726,6 +9227,14 @@ dependencies: zod "3.22.4" +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -5735,16 +9244,31 @@ "@webassemblyjs/helper-wasm-bytecode" "1.9.0" "@webassemblyjs/wast-parser" "1.9.0" +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + "@webassemblyjs/floating-point-hex-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + "@webassemblyjs/helper-api-error@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + "@webassemblyjs/helper-buffer@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" @@ -5769,11 +9293,35 @@ dependencies: "@webassemblyjs/ast" "1.9.0" +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + "@webassemblyjs/helper-wasm-bytecode@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/helper-wasm-section@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" @@ -5784,6 +9332,13 @@ "@webassemblyjs/helper-wasm-bytecode" "1.9.0" "@webassemblyjs/wasm-gen" "1.9.0" +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + "@webassemblyjs/ieee754@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" @@ -5791,6 +9346,13 @@ dependencies: "@xtuc/ieee754" "^1.2.0" +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + "@webassemblyjs/leb128@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" @@ -5798,6 +9360,11 @@ dependencies: "@xtuc/long" "4.2.2" +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + "@webassemblyjs/utf8@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" @@ -5817,6 +9384,31 @@ "@webassemblyjs/wasm-parser" "1.9.0" "@webassemblyjs/wast-printer" "1.9.0" +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + "@webassemblyjs/wasm-gen@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" @@ -5828,6 +9420,16 @@ "@webassemblyjs/leb128" "1.9.0" "@webassemblyjs/utf8" "1.9.0" +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wasm-opt@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" @@ -5838,6 +9440,18 @@ "@webassemblyjs/wasm-gen" "1.9.0" "@webassemblyjs/wasm-parser" "1.9.0" +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + "@webassemblyjs/wasm-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" @@ -5862,6 +9476,14 @@ "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + "@webassemblyjs/wast-printer@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" @@ -5901,11 +9523,21 @@ dependencies: argparse "^2.0.1" -abab@^2.0.6: +abab@^2.0.5, abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== + abbrev@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" @@ -5921,12 +9553,32 @@ abitype@1.0.0: resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + abortcontroller-polyfill@^1.7.5: version "1.7.5" resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3, abstract-level@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.4.tgz#3ad8d684c51cc9cbc9cf9612a7100b716c414b57" + integrity sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg== + dependencies: + buffer "^6.0.3" + catering "^2.1.0" + is-buffer "^2.0.5" + level-supports "^4.0.0" + level-transcoder "^1.0.1" + module-error "^1.0.1" + queue-microtask "^1.2.3" + +accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -5942,6 +9594,16 @@ acorn-globals@^7.0.0: acorn "^8.1.0" acorn-walk "^8.0.2" +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + acorn-jsx@^5.2.0, acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -5967,7 +9629,7 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.1.0, acorn@^8.11.3, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.1, acorn@^8.9.0: +acorn@^8.1.0, acorn@^8.11.3, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -5977,6 +9639,11 @@ address@^1.0.1, address@^1.1.2: resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -5989,6 +9656,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -6002,12 +9676,26 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4: +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -6017,11 +9705,62 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.0.1, ajv@^8.12.0, ajv@^8.9.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.13.0.tgz#a3939eaec9fb80d217ddf0c3376948c023f28c91" + integrity sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +alchemy-sdk@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/alchemy-sdk/-/alchemy-sdk-3.3.1.tgz#a0cbd670f95c8a948966d3d4dba242b68723a071" + integrity sha512-iH/wIhBsHr18NTV9G9WrNsk/ofBOrhKaxH1vG9IZN3t+sTrB5uKAMMgmKvvJHDnOJ2Fo/bTnYPgUWNqhQxEfCQ== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/contracts" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/providers" "^5.7.0" + "@ethersproject/units" "^5.7.0" + "@ethersproject/wallet" "^5.7.0" + "@ethersproject/web" "^5.7.0" + axios "^1.6.5" + sturdy-websocket "^0.2.1" + websocket "^1.0.34" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ== +amazon-cognito-identity-js@^6.0.1: + version "6.3.12" + resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.12.tgz#af73df033094ad4c679c19cf6122b90058021619" + integrity sha512-s7NKDZgx336cp+oDeUtB2ZzT8jWJp/v2LWuYl+LQtMEODe22RF1IJ4nRiDATp+rp1pTffCZcm44Quw4jx2bqNg== + dependencies: + "@aws-crypto/sha256-js" "1.2.2" + buffer "4.9.2" + fast-base64-decode "^1.0.0" + isomorphic-unfetch "^3.0.0" + js-cookie "^2.2.1" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" @@ -6039,7 +9778,7 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: dependencies: type-fest "^0.21.3" -ansi-html-community@0.0.8: +ansi-html-community@0.0.8, ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== @@ -6049,6 +9788,11 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + ansi-regex@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" @@ -6093,6 +9837,16 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +any-base@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" + integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -6114,6 +9868,16 @@ anymatch@^3.0.0, anymatch@^3.0.3, anymatch@^3.1.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -6124,6 +9888,14 @@ arch@^2.1.1, arch@^2.2.0: resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -6156,6 +9928,16 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1, array-back@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" @@ -6197,7 +9979,12 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.1: +array-union@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" + integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== + +array-uniq@1.0.3, array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== @@ -6274,6 +10061,11 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +asap@^2.0.0, asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + asn1.js@^5.2.0: version "5.4.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" @@ -6347,6 +10139,18 @@ async-mutex@^0.2.6: dependencies: tslib "^2.0.0" +async-retry@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== + dependencies: + retry "0.13.1" + +async@1.x: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + async@^2.6.4: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -6354,7 +10158,7 @@ async@^2.6.4: dependencies: lodash "^4.17.14" -async@^3.2.0, async@^3.2.3: +async@^3.0.0, async@^3.2.0, async@^3.2.3: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== @@ -6379,6 +10183,18 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +autoprefixer@^10.4.9: + version "10.4.19" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f" + integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew== + dependencies: + browserslist "^4.23.0" + caniuse-lite "^1.0.30001599" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + autoprefixer@^9.8.6: version "9.8.8" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" @@ -6409,6 +10225,18 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== +axios-better-stacktrace@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/axios-better-stacktrace/-/axios-better-stacktrace-2.1.6.tgz#8d6586b101af56fb890e2a4e3e37963229b05b63" + integrity sha512-t0oR9MU9H5IHjEA2TggmwYBdeBmvJw5krrKhOipiIjwZEoUpViuuzB+4uZsOholmZnljFUPPKE2wTGxILBmB+Q== + +axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + axios@^0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" @@ -6416,6 +10244,15 @@ axios@^0.24.0: dependencies: follow-redirects "^1.14.4" +axios@^1.4.0, axios@^1.5.1, axios@^1.6.2, axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axios@^1.6.0, axios@^1.6.5: version "1.6.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" @@ -6457,6 +10294,14 @@ babel-loader@^8.1.0: make-dir "^3.1.0" schema-utils "^2.6.5" +babel-loader@^9.1.2: + version "9.1.3" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.3.tgz#3d0e01b4e69760cc694ee306fe16d358aa1c6f9a" + integrity sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw== + dependencies: + find-cache-dir "^4.0.0" + schema-utils "^4.0.0" + babel-plugin-const-enum@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz#3d25524106f68f081e187829ba736b251c289861" @@ -6512,6 +10357,15 @@ babel-plugin-macros@^3.1.0: cosmiconfig "^7.0.0" resolve "^1.19.0" +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.11" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" + integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.6.2" + semver "^6.3.1" + babel-plugin-polyfill-corejs2@^0.4.8: version "0.4.8" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" @@ -6521,6 +10375,14 @@ babel-plugin-polyfill-corejs2@^0.4.8: "@babel/helper-define-polyfill-provider" "^0.5.0" semver "^6.3.1" +babel-plugin-polyfill-corejs3@^0.10.4: + version "0.10.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz#789ac82405ad664c20476d0233b485281deb9c77" + integrity sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.1" + core-js-compat "^3.36.1" + babel-plugin-polyfill-corejs3@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz#9eea32349d94556c2ad3ab9b82ebb27d4bf04a81" @@ -6536,6 +10398,13 @@ babel-plugin-polyfill-regenerator@^0.5.5: dependencies: "@babel/helper-define-polyfill-provider" "^0.5.0" +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" + integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.2" + babel-plugin-transform-typescript-metadata@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz#7a327842d8c36ffe07ee1b5276434e56c297c9b7" @@ -6581,7 +10450,7 @@ base-x@^3.0.2, base-x@^3.0.8: dependencies: safe-buffer "^5.0.1" -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -6599,7 +10468,7 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@^2.0.1: +basic-auth@^2.0.1, basic-auth@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== @@ -6618,6 +10487,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcrypt@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2" + integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.11" + node-addon-api "^5.0.0" + bech32@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -6648,7 +10525,12 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bignumber.js@^9.0.0: +bigint-crypto-utils@^3.0.23: + version "3.3.0" + resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz#72ad00ae91062cf07f2b1def9594006c279c1d77" + integrity sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg== + +bignumber.js@^9.0.0, bignumber.js@^9.0.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -6699,7 +10581,7 @@ bn.js@4.11.6: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.6, bn.js@^4.11.9: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.12.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -6727,7 +10609,7 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" -body-parser@^1.16.0: +body-parser@1.20.2, body-parser@^1.16.0: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== @@ -6745,6 +10627,14 @@ body-parser@^1.16.0: type-is "~1.6.18" unpipe "1.0.0" +bonjour-service@^1.0.11: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" @@ -6775,6 +10665,11 @@ bootstrap@5.3: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.3.tgz#de35e1a765c897ac940021900fcbb831602bac38" integrity sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg== +bottleneck@^2.19.5: + version "2.19.5" + resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" + integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== + bowser@^2.11.0, bowser@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -6823,6 +10718,16 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== +browser-level@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" + integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.1" + module-error "^1.0.2" + run-parallel-limit "^1.1.0" + browser-resolve@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-2.0.0.tgz#99b7304cb392f8d73dba741bb2d7da28c6d7842b" @@ -6830,6 +10735,11 @@ browser-resolve@^2.0.0: dependencies: resolve "^1.17.0" +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -6891,7 +10801,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.22.2, browserslist@^4.22.3: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2, browserslist@^4.22.3, browserslist@^4.23.0: version "4.23.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== @@ -6908,7 +10818,7 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" -bs58@^4.0.0: +bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== @@ -6931,11 +10841,28 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +bson@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/bson/-/bson-4.7.2.tgz#320f4ad0eaf5312dd9b45dc369cc48945e2a5f2e" + integrity sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ== + dependencies: + buffer "^5.6.0" + +bson@^6.4.0, bson@^6.5.0, bson@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/bson/-/bson-6.7.0.tgz#51973b132cdc424c8372fda3cb43e3e3e2ae2227" + integrity sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ== + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -6961,15 +10888,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@6.0.3, buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@^4.3.0: +buffer@4.9.2, buffer@^4.3.0: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== @@ -6978,6 +10897,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@6.0.3, buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -7010,6 +10937,13 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" +busboy@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -7064,6 +10998,24 @@ cacache@^12.0.2, cacache@^12.0.3: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^18.0.0: + version "18.0.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.3.tgz#864e2c18414e1e141ae8763f31e46c2cb96d1b21" + integrity sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -7079,6 +11031,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + cache-loader@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-4.1.0.tgz#9948cae353aec0a1fcb1eafda2300816ec85387e" @@ -7149,6 +11109,11 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" +callsite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ== + callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -7172,7 +11137,7 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0, camelcase@^6.3.0: +camelcase@^6.0.0, camelcase@^6.2.0, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -7192,17 +11157,55 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001587: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz#7ad6dba4c9bf6561aec8291976402339dc157dfb" integrity sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg== +caniuse-lite@^1.0.30001599: + version "1.0.30001618" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz#fad74fa006aef0f01e8e5c0a5540c74d8d36ec6f" + integrity sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg== + +canvas@^2.11.2: + version "2.11.2" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" + integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.0" + nan "^2.17.0" + simple-get "^3.0.3" + case-sensitive-paths-webpack-plugin@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== -caseless@~0.12.0: +case@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== + +caseless@^0.12.0, caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -chai@^4.3.10: +catering@^2.1.0, catering@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" + integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== + +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + +chai-as-promised@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.2.tgz#70cd73b74afd519754161386421fb71832c6d041" + integrity sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw== + dependencies: + check-error "^1.0.2" + +chai@^4.2.0, chai@^4.3.10: version "4.4.1" resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== @@ -7256,6 +11259,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +"charenc@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + chart.js@^4.4.0: version "4.4.1" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.1.tgz#ac5dc0e69a7758909158a96fe80ce43b3bb96a9f" @@ -7263,7 +11271,12 @@ chart.js@^4.4.0: dependencies: "@kurkle/color" "^0.3.0" -check-error@^1.0.3: +check-disk-space@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/check-disk-space/-/check-disk-space-3.4.0.tgz#eb8e69eee7a378fd12e35281b8123a8b4c4a8ff7" + integrity sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw== + +check-error@^1.0.2, check-error@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== @@ -7280,7 +11293,22 @@ check-types@^8.0.3: resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.4.1, chokidar@^3.5.3: +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -7319,6 +11347,11 @@ chownr@^1.1.1, chownr@^1.1.4: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -7329,12 +11362,17 @@ ci-info@^1.5.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -cids@^0.7.1: +cids@^0.7.1, cids@~0.7.0: version "0.7.5" resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== @@ -7345,6 +11383,17 @@ cids@^0.7.1: multicodec "^1.0.0" multihashes "~0.4.15" +cids@~0.8.0: + version "0.8.3" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.8.3.tgz#aaf48ac8ed857c3d37dad94d8db1d8c9407b92db" + integrity sha512-yoXTbV3llpm+EBGWKeL9xKtksPE/s6DPoDSY4fn8I8TEW1zehWXPSB0pwAXVDlLaOlrw+sNynj995uD9abmPhA== + dependencies: + buffer "^5.6.0" + class-is "^1.1.0" + multibase "^1.0.0" + multicodec "^1.0.1" + multihashes "^1.0.1" + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -7365,11 +11414,21 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== +cjs-module-lexer@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" + integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== + class-is@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== +class-transformer@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -7380,6 +11439,37 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +class-validator-jsonschema@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/class-validator-jsonschema/-/class-validator-jsonschema-5.0.0.tgz#0c7e4a1825597365a77bbe298e805dc083fce21d" + integrity sha512-F1skc5+NHZUxtVH56js1wdPKayUoIEZNpiZeNYIAJO0L6hCODmlX+lXwb26RRqTrjo0U24tNkSujn/G0zOvZoQ== + dependencies: + lodash.groupby "^4.6.0" + lodash.merge "^4.6.2" + openapi3-ts "^3.0.0" + reflect-metadata "^0.1.13" + tslib "^2.4.1" + +class-validator@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" + integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== + dependencies: + "@types/validator" "^13.11.8" + libphonenumber-js "^1.10.53" + validator "^13.9.0" + +classic-level@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.4.1.tgz#169ecf9f9c6200ad42a98c8576af449c1badbaee" + integrity sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.0" + module-error "^1.0.1" + napi-macros "^2.2.2" + node-gyp-build "^4.3.0" + clean-css@4.2.x: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -7428,6 +11518,25 @@ cli-spinners@^2.0.0, cli-spinners@^2.5.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== +cli-table3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + +cli-table3@^0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-table3@~0.6.1: version "0.6.3" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" @@ -7603,7 +11712,12 @@ color-string@^1.6.0, color-string@^1.9.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color@^3.0.0: +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +color@^3.0.0, color@^3.1.3: version "3.2.1" resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== @@ -7619,11 +11733,34 @@ color@^4.2.3: color-convert "^2.0.1" color-string "^1.9.0" -colorette@^2.0.16, colorette@^2.0.20: +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.10, colorette@^2.0.16, colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colors@1.0.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== + +colors@1.4.0, colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + columnify@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -7639,11 +11776,41 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +command-line-args@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-usage@^6.1.0: + version "6.1.3" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== + dependencies: + array-back "^4.0.2" + chalk "^2.4.2" + table-layout "^1.0.2" + typical "^5.2.0" + commander@2.17.x: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -7659,11 +11826,26 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^9.1.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + common-tags@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -7674,7 +11856,7 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -component-emitter@^1.2.1: +component-emitter@^1.2.1, component-emitter@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== @@ -7704,7 +11886,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.5.0: +concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@^1.6.0, concat-stream@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -7714,6 +11896,16 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + condense-newlines@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" @@ -7741,6 +11933,11 @@ connect-history-api-fallback@^1.6.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + consola@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" @@ -7751,6 +11948,11 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + consolidate@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" @@ -7763,7 +11965,7 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== -content-disposition@0.5.4: +content-disposition@0.5.4, content-disposition@~0.5.2: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -7779,7 +11981,7 @@ content-hash@^2.5.2: multicodec "^0.5.5" multihashes "^0.4.15" -content-type@~1.0.4, content-type@~1.0.5: +content-type@^1.0.4, content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -7809,6 +12011,29 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cookiejar@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== + +cookies@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3" + integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + copy-anything@^2.0.1: version "2.0.6" resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" @@ -7833,6 +12058,18 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== +copy-webpack-plugin@^10.2.4: + version "10.2.4" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz#6c854be3fdaae22025da34b9112ccf81c63308fe" + integrity sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg== + dependencies: + fast-glob "^3.2.7" + glob-parent "^6.0.1" + globby "^12.0.2" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + copy-webpack-plugin@^5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz#8a889e1dcafa6c91c6cd4be1ad158f1d3823bae2" @@ -7858,6 +12095,18 @@ core-js-compat@^3.31.0, core-js-compat@^3.34.0, core-js-compat@^3.6.5: dependencies: browserslist "^4.22.3" +core-js-compat@^3.36.1: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.37.1.tgz#c844310c7852f4bdf49b8d339730b97e17ff09ee" + integrity sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg== + dependencies: + browserslist "^4.23.0" + +core-js-pure@^3.23.3: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.37.1.tgz#2b4b34281f54db06c9a9a5bd60105046900553bd" + integrity sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA== + core-js@^3.6.5: version "3.36.0" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.36.0.tgz#e752fa0b0b462a0787d56e9d73f80b0f7c0dde68" @@ -7907,7 +12156,7 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" -cosmiconfig@^7.0.0: +cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== @@ -7918,6 +12167,16 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^8.1.3: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + crc-32@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" @@ -7972,6 +12231,13 @@ create-require@^1.1.0, create-require@^1.1.1: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cron-parser@^4: + version "4.9.0" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.9.0.tgz#0340694af3e46a0894978c6f52a6dbb5c0f11ad5" + integrity sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q== + dependencies: + luxon "^3.2.1" + cross-fetch@^3.0.6, cross-fetch@^3.1.4, cross-fetch@^3.1.5: version "3.1.8" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" @@ -8020,6 +12286,11 @@ crossws@^0.1.0: resolved "https://registry.yarnpkg.com/crossws/-/crossws-0.1.1.tgz#3a85a8140568e4828d9747a884171ea7e6a8bbe2" integrity sha512-c9c/o7bS3OjsdpSkvexpka0JNlesBF2JU9B2V1yNsYGwRbAafxhJQ7VI9b48D5bpONz/oxbPGMzBojy9sXoQIQ== +"crypt@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -8055,6 +12326,11 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" +css-declaration-sorter@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz#6dec1c9523bc4a643e088aab8f09e67a54961024" + integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== + css-loader@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" @@ -8074,6 +12350,32 @@ css-loader@^3.5.3: schema-utils "^2.7.0" semver "^6.3.0" +css-loader@^6.4.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" + integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + +css-minimizer-webpack-plugin@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz#33effe662edb1a0bf08ad633c32fa75d0f7ec565" + integrity sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + cssnano "^6.0.1" + jest-worker "^29.4.3" + postcss "^8.4.24" + schema-utils "^4.0.1" + serialize-javascript "^6.0.1" + css-parse@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" @@ -8107,6 +12409,17 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" @@ -8123,12 +12436,28 @@ css-tree@^1.1.2: mdn-data "2.0.14" source-map "^0.6.1" +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + css-what@^3.2.1: version "3.4.2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== -css-what@^6.0.1: +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -8184,6 +12513,42 @@ cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.8: postcss-svgo "^4.0.3" postcss-unique-selectors "^4.0.1" +cssnano-preset-default@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz#adf4b89b975aa775f2750c89dbaf199bbd9da35e" + integrity sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg== + dependencies: + browserslist "^4.23.0" + css-declaration-sorter "^7.2.0" + cssnano-utils "^4.0.2" + postcss-calc "^9.0.1" + postcss-colormin "^6.1.0" + postcss-convert-values "^6.1.0" + postcss-discard-comments "^6.0.2" + postcss-discard-duplicates "^6.0.3" + postcss-discard-empty "^6.0.3" + postcss-discard-overridden "^6.0.2" + postcss-merge-longhand "^6.0.5" + postcss-merge-rules "^6.1.1" + postcss-minify-font-values "^6.1.0" + postcss-minify-gradients "^6.0.3" + postcss-minify-params "^6.1.0" + postcss-minify-selectors "^6.0.4" + postcss-normalize-charset "^6.0.2" + postcss-normalize-display-values "^6.0.2" + postcss-normalize-positions "^6.0.2" + postcss-normalize-repeat-style "^6.0.2" + postcss-normalize-string "^6.0.2" + postcss-normalize-timing-functions "^6.0.2" + postcss-normalize-unicode "^6.1.0" + postcss-normalize-url "^6.0.2" + postcss-normalize-whitespace "^6.0.2" + postcss-ordered-values "^6.0.2" + postcss-reduce-initial "^6.1.0" + postcss-reduce-transforms "^6.0.2" + postcss-svgo "^6.0.3" + postcss-unique-selectors "^6.0.4" + cssnano-util-get-arguments@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" @@ -8206,6 +12571,11 @@ cssnano-util-same-parent@^4.0.0: resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== +cssnano-utils@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.2.tgz#56f61c126cd0f11f2eef1596239d730d9fceff3c" + integrity sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ== + cssnano@^4.0.0, cssnano@^4.1.10: version "4.1.11" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99" @@ -8216,6 +12586,14 @@ cssnano@^4.0.0, cssnano@^4.1.10: is-resolvable "^1.0.0" postcss "^7.0.0" +cssnano@^6.0.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-6.1.2.tgz#4bd19e505bd37ee7cf0dc902d3d869f6d79c66b8" + integrity sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA== + dependencies: + cssnano-preset-default "^6.1.2" + lilconfig "^3.1.1" + csso@^4.0.2: version "4.2.0" resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" @@ -8223,6 +12601,13 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + cssom@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" @@ -8252,15 +12637,20 @@ csstype@^3.0.2, csstype@^3.1.3, csstype@latest: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +cycle@1.0.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" + integrity sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA== + cyclist@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== -cypress@13.6.4: - version "13.6.4" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.4.tgz#42c88d3ee0342f1681abfacabf9c1f082676bc53" - integrity sha512-pYJjCfDYB+hoOoZuhysbbYhEmNW7DEDsqn+ToCLwuVowxUXppIWRr7qk4TVRIU471ksfzyZcH+mkoF0CQUKnpw== +cypress@^13.6.6: + version "13.9.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.9.0.tgz#b529cfa8f8c39ba163ed0501a25bb5b09c143652" + integrity sha512-atNjmYfHsvTuCaxTxLZr9xGoHz53LLui3266WWxXJHY7+N6OdwJdg/feEa3T+buez9dmUXHT1izCOklqG82uCQ== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" @@ -8269,7 +12659,7 @@ cypress@13.6.4: arch "^2.2.0" blob-util "^2.0.2" bluebird "^3.7.2" - buffer "^5.6.0" + buffer "^5.7.1" cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" @@ -8287,7 +12677,7 @@ cypress@13.6.4: figures "^3.2.0" fs-extra "^9.1.0" getos "^3.2.1" - is-ci "^3.0.0" + is-ci "^3.0.1" is-installed-globally "~0.4.0" lazy-ass "^1.6.0" listr2 "^3.8.3" @@ -8343,18 +12733,35 @@ date-fns-tz@^1.3.7: resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.3.8.tgz#083e3a4e1f19b7857fa0c18deea6c2bc46ded7b9" integrity sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ== -date-fns@^2.29.3, date-fns@^2.30.0: +date-fns@^2.28.0, date-fns@^2.29.3, date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== dependencies: "@babel/runtime" "^7.21.0" +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +date.js@~0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/date.js/-/date.js-0.3.3.tgz#ef1e92332f507a638795dbb985e951882e50bbda" + integrity sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw== + dependencies: + debug "~3.1.0" + dayjs@1.11.10, dayjs@^1.10.4: version "1.11.10" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== +death@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" + integrity sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -8362,7 +12769,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4, debug@4.3.4, debug@4.x, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -8388,6 +12795,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + decimal.js-light@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" @@ -8410,6 +12822,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -8422,7 +12841,7 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== -deep-eql@^4.1.3: +deep-eql@^4.0.1, deep-eql@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== @@ -8441,7 +12860,17 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.5.1" -deep-is@^0.1.3: +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== + +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -8451,7 +12880,7 @@ deepmerge@^1.5.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== -deepmerge@^4.2.2: +deepmerge@^4.2.2, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== @@ -8471,6 +12900,13 @@ default-gateway@^5.0.5: dependencies: execa "^3.3.0" +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -8556,12 +12992,17 @@ delegate@^3.1.2: resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + denque@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@2.0.0: +depd@2.0.0, depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -8589,7 +13030,7 @@ destr@^2.0.1, destr@^2.0.2: resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449" integrity sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ== -destroy@1.2.0: +destroy@1.2.0, destroy@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -8604,6 +13045,11 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== +detect-libc@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -8622,11 +13068,24 @@ detect-port@^1.5.1: address "^1.0.1" debug "4" +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -8641,6 +13100,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== + dependencies: + heap ">= 0.2.0" + dijkstrajs@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23" @@ -8660,6 +13126,29 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +discord-api-types@0.37.83: + version "0.37.83" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.83.tgz#a22a799729ceded8176ea747157837ddf4708b1f" + integrity sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA== + +discord.js@^14.15.2: + version "14.15.2" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.15.2.tgz#ea2cacad02aff1faedf69ccea012149e06ab4277" + integrity sha512-wGD37YCaTUNprtpqMIRuNiswwsvSWXrHykBSm2SAosoTYut0VUDj9yo9t4iLtMKvuhI49zYkvKc2TNdzdvpJhg== + dependencies: + "@discordjs/builders" "^1.8.1" + "@discordjs/collection" "1.5.3" + "@discordjs/formatters" "^0.4.0" + "@discordjs/rest" "^2.3.0" + "@discordjs/util" "^1.1.0" + "@discordjs/ws" "^1.1.0" + "@sapphire/snowflake" "3.5.3" + discord-api-types "0.37.83" + fast-deep-equal "3.1.3" + lodash.snakecase "4.1.1" + tslib "2.6.2" + undici "6.13.0" + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -8673,6 +13162,13 @@ dns-packet@^1.3.1: ip "^1.1.0" safe-buffer "^5.0.1" +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + dns-txt@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" @@ -8718,6 +13214,15 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" @@ -8738,7 +13243,7 @@ domelementtype@1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@^2.0.1, domelementtype@^2.2.0: +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -8757,6 +13262,13 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -8774,6 +13286,23 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -8849,6 +13378,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + eciesjs@^0.3.15, eciesjs@^0.3.16: version "0.3.18" resolved "https://registry.yarnpkg.com/eciesjs/-/eciesjs-0.3.18.tgz#67b5d73a8466e40a45bbc2f2a3177e71e9c0643d" @@ -8885,12 +13421,19 @@ ejs@^3.1.7: dependencies: jake "^10.8.5" +ejs@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.668: version "1.4.680" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.680.tgz#18a30d3f557993eda2d5b1e21a06c4d51875392f" integrity sha512-4nToZ5jlPO14W82NkF32wyjhYqQByVaDmLy4J2/tYcAbJfgO2TKJC780Az1V13gzq4l73CJ0yuyalpXvxXXD9A== -elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.4, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -8903,6 +13446,19 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.3, elliptic@^6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.4.0, elliptic@^6.5.2: + version "6.5.5" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" + integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -8933,17 +13489,22 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + encode-utf8@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== -encodeurl@~1.0.2: +encodeurl@^1.0.2, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding@^0.1.11: +encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -8982,6 +13543,14 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0: memory-fs "^0.5.0" tapable "^1.0.0" +enhanced-resolve@^5.0.0, enhanced-resolve@^5.16.0: + version "5.16.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz#e8bc63d51b826d6f1cbc0a150ecb5a8b0c62e567" + integrity sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enhanced-resolve@^5.7.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" @@ -8990,7 +13559,7 @@ enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@^2.3.6: +enquirer@^2.3.0, enquirer@^2.3.6: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== @@ -9010,11 +13579,21 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.4.0, entities@^4.5.0: +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + errno@^0.1.1, errno@^0.1.3, errno@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -9100,6 +13679,11 @@ es-errors@^1.0.0, es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-module-lexer@^1.2.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.2.tgz#00b423304f2500ac59359cc9b6844951f372d497" + integrity sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA== + es-set-tostringtag@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" @@ -9134,6 +13718,16 @@ es5-ext@^0.10.35, es5-ext@^0.10.50: es6-symbol "^3.1.3" next-tick "^1.1.0" +es5-ext@^0.10.62, es5-ext@^0.10.63, es5-ext@~0.10.14: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + es6-iterator@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" @@ -9143,7 +13737,7 @@ es6-iterator@^2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-promise@^4.2.8: +es6-promise@^4.2.4, es6-promise@^4.2.8: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== @@ -9156,7 +13750,7 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: d "^1.0.1" ext "^1.1.2" -esbuild@^0.19.3: +esbuild@^0.19.2, esbuild@^0.19.3: version "0.19.12" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== @@ -9190,7 +13784,7 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== @@ -9200,15 +13794,27 @@ escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A== + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" escodegen@^2.0.0: version "2.1.0" @@ -9301,6 +13907,14 @@ eslint-plugin-vue@^7.0.0-0: semver "^6.3.0" vue-eslint-parser "^7.10.0" +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -9309,14 +13923,6 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - eslint-scope@^7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" @@ -9342,50 +13948,7 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@8.48.0: - version "8.48.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.48.0.tgz#bf9998ba520063907ba7bfe4c480dc8be03c2155" - integrity sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "8.48.0" - "@humanwhocodes/config-array" "^0.11.10" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - -eslint@^8.0.0: +eslint@8.57.0, eslint@^8.0.0: version "8.57.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -9429,6 +13992,16 @@ eslint@^8.0.0: strip-ansi "^6.0.1" text-table "^0.2.0" +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + espree@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -9447,6 +14020,11 @@ espree@^9.0.0, espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +esprima@2.7.x, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -9466,6 +14044,11 @@ esrecurse@^4.1.0, esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== + estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -9517,6 +14100,25 @@ eth-ens-namehash@2.0.8: idna-uts46-hx "^2.3.1" js-sha3 "^0.5.7" +eth-gas-reporter@^0.2.25: + version "0.2.27" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz#928de8548a674ed64c7ba0bf5795e63079150d4e" + integrity sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw== + dependencies: + "@solidity-parser/parser" "^0.14.0" + axios "^1.5.1" + cli-table3 "^0.5.0" + colors "1.4.0" + ethereum-cryptography "^1.0.3" + ethers "^5.7.2" + fs-readdir-recursive "^1.1.0" + lodash "^4.17.14" + markdown-table "^1.1.3" + mocha "^10.2.0" + req-cwd "^2.0.0" + sha1 "^1.1.1" + sync-request "^6.0.0" + eth-json-rpc-filters@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/eth-json-rpc-filters/-/eth-json-rpc-filters-6.0.1.tgz#0b3e370f017f5c6f58d3e7bd0756d8099ed85c56" @@ -9571,7 +14173,7 @@ ethereum-bloom-filters@^1.0.6: dependencies: js-sha3 "^0.8.0" -ethereum-cryptography@^0.1.3: +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -9592,6 +14194,16 @@ ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" +ethereum-cryptography@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" + integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/bip32" "1.1.5" + "@scure/bip39" "1.1.1" + ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" @@ -9602,7 +14214,28 @@ ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: "@scure/bip32" "1.3.3" "@scure/bip39" "1.2.2" -ethereumjs-util@^7.1.5: +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -9613,7 +14246,7 @@ ethereumjs-util@^7.1.5: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@5.7.2: +ethers@5.7.2, ethers@^5.7.1, ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -9657,11 +14290,32 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" +ethjs-util@0.1.6, ethjs-util@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + event-pubsub@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter2@6.4.7: version "6.4.7" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" @@ -9687,7 +14341,7 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0, events@^3.3.0: +events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -9828,7 +14482,94 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -express@^4.14.0, express@^4.16.3, express@^4.17.1: +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + +express-async-errors@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/express-async-errors/-/express-async-errors-3.1.1.tgz#6053236d61d21ddef4892d6bd1d736889fc9da41" + integrity sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng== + +express-ejs-layouts@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/express-ejs-layouts/-/express-ejs-layouts-2.5.1.tgz#d204d9065ee2825fcbd718d820289fc81e691ccb" + integrity sha512-IXROv9n3xKga7FowT06n1Qn927JR8ZWDn5Dc9CJQoiiaaDqbhW5PDmWShzbpAa2wjWT1vJqaIM1S6vJwwX11gA== + +express-jwt-permissions@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/express-jwt-permissions/-/express-jwt-permissions-1.3.7.tgz#af16fc6949a87f02535ed52d218f01098ad882db" + integrity sha512-FjmznwcOl4O2xff1gK6ASGDU92IbLAH3AsdJ5nfn0zm8B3TXMMa0Y2+5FMSxpeMt0H70y/3eRTm4KR2UBsvkQA== + dependencies: + express-unless "^2.0.0" + lodash.get "^4.4.2" + +express-jwt@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-8.4.1.tgz#ba817c1ced7c6f1f7017fc2e6deac207011e8acb" + integrity sha512-IZoZiDv2yZJAb3QrbaSATVtTCYT11OcqgFGoTN4iKVyN6NBkBkhtVIixww5fmakF0Upt5HfOxJuS6ZmJVeOtTQ== + dependencies: + "@types/jsonwebtoken" "^9" + express-unless "^2.1.3" + jsonwebtoken "^9.0.0" + +express-rate-limit@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.2.0.tgz#06ce387dd5388f429cab8263c514fc07bf90a445" + integrity sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg== + +express-unless@^2.0.0, express-unless@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-2.1.3.tgz#f951c6cca52a24da3de32d42cfd4db57bc0f9a2e" + integrity sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ== + +express-validator@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-7.0.1.tgz#435fac6b5fa838763f78eca05d2206317b92106e" + integrity sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA== + dependencies: + lodash "^4.17.21" + validator "^13.9.0" + +express@^4.14.0, express@^4.17.3: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +express@^4.16.3, express@^4.17.1: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -9865,6 +14606,43 @@ express@^4.14.0, express@^4.16.3, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +express@~4.18.1: + version "4.18.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.3.tgz#6870746f3ff904dee1819b82e4b51509afffb0d4" + integrity sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + ext@^1.1.2: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -9887,7 +14665,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: +extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -9946,16 +14724,75 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w== +eyes@0.1.x: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + +fabric-ca-client@^2.2.20: + version "2.2.20" + resolved "https://registry.yarnpkg.com/fabric-ca-client/-/fabric-ca-client-2.2.20.tgz#c6d3d442b8d82bdf0cd65e65e2100d5d9ba3409b" + integrity sha512-uaVpjPU+Yar2n4sNPJZpRC/jPgeLhw7nbzGl+aInkleg8Zkor4eBfQKpNQQxEORqgqWS5hAHNYu2x0iPEHBmLw== + dependencies: + fabric-common "2.2.20" + jsrsasign "^10.5.25" + url "^0.11.0" + winston "^2.4.5" + +fabric-common@2.2.20: + version "2.2.20" + resolved "https://registry.yarnpkg.com/fabric-common/-/fabric-common-2.2.20.tgz#f96bec99e21364d832113f11e7df15e737b8374c" + integrity sha512-d7oPqXrEIHlN0yhno1iTSX1aRD0ews5Oq1kdjrUF5TWjV/lQ8ojFNxLb0gDICBlM5C4gYtFNfFt6rD4uvpmbuw== + dependencies: + callsite "^1.0.0" + elliptic "^6.5.4" + fabric-protos "2.2.20" + js-sha3 "^0.9.2" + jsrsasign "^10.5.25" + long "^5.2.3" + nconf "^0.12.0" + promise-settle "^0.3.0" + sjcl "^1.0.8" + winston "^2.4.5" + yn "^4.0.0" + optionalDependencies: + pkcs11js "^1.3.0" -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fabric-network@^2.2.20: + version "2.2.20" + resolved "https://registry.yarnpkg.com/fabric-network/-/fabric-network-2.2.20.tgz#da51ab0d871e2cdfc51643b2b58900222955ca05" + integrity sha512-Hdecb9UBuY/M2FZI4I7mYNRejYrULqA/VBiFIX3zLaQ7z7HVSfZOjFb9o+YepygBES1NSn/e+hSDfAFcziPSQQ== + dependencies: + fabric-common "2.2.20" + fabric-protos "2.2.20" + long "^5.2.3" + nano "^10.1.2" + +fabric-protos@2.2.20: + version "2.2.20" + resolved "https://registry.yarnpkg.com/fabric-protos/-/fabric-protos-2.2.20.tgz#6fac9d95096b40f734fab7384a3c01b574d15625" + integrity sha512-fTBpmR0RorMNX29Ks1I/1vpB3ktW1ooDV6mFUiPzaZTk60tpi9YVfPUIIYb3vtPxYSdjD1MZkUaxsMzujDwRoA== + dependencies: + "@grpc/grpc-js" "~1.9.0" + "@grpc/proto-loader" "^0.7.0" + long "^5.2.3" + protobufjs "^7.2.0" + +fast-base64-decode@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" + integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== + +fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w== + fast-diff@^1.1.2: version "1.3.0" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" @@ -9984,7 +14821,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: +fast-glob@^3.0.3, fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -10000,7 +14837,7 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-sta resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -10010,11 +14847,18 @@ fast-redact@^3.0.0: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.3.0.tgz#7c83ce3a7be4898241a46560d51de10f653f7634" integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== -fast-safe-stringify@^2.0.6: +fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-xml-parser@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" + integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -10043,6 +14887,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + fflate@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" @@ -10145,6 +14994,26 @@ find-cache-dir@^3.0.0, find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-cache-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" + integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== + dependencies: + common-path-prefix "^3.0.0" + pkg-dir "^7.0.0" + +find-package-json@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083" + integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw== + +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -10158,6 +15027,13 @@ find-up@5.0.0, find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== + dependencies: + locate-path "^2.0.0" + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -10173,6 +15049,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== + dependencies: + locate-path "^7.1.0" + path-exists "^5.0.0" + flat-cache@^3.0.4: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" @@ -10187,7 +15071,7 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.2.9: +flatted@^3.2.7, flatted@^3.2.9: version "3.3.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== @@ -10200,11 +15084,26 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +fn-args@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fn-args/-/fn-args-5.0.0.tgz#7a18e105c8fb3bf0a51c30389bf16c9ebe740bb3" + integrity sha512-CtbfI3oFFc3nbdIoHycrfbrxiGgxXBXXuyOl49h47JawM1mYrqpiRqnH5CB2mBatdXvHHOUO6a+RiAuuvKt0lw== + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + follow-redirects@^1.0.0, follow-redirects@^1.14.4, follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -10255,6 +15154,24 @@ forever-agent@~0.6.1: semver "^7.3.2" tapable "^1.0.0" +fork-ts-checker-webpack-plugin@7.2.13: + version "7.2.13" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz#51ffd6a2f96f03ab64b92f8aedf305dbf3dee0f1" + integrity sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg== + dependencies: + "@babel/code-frame" "^7.16.7" + chalk "^4.1.2" + chokidar "^3.5.3" + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + fs-extra "^10.0.0" + memfs "^3.4.1" + minimatch "^3.0.4" + node-abort-controller "^3.0.1" + schema-utils "^3.1.1" + semver "^7.3.5" + tapable "^2.2.1" + fork-ts-checker-webpack-plugin@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" @@ -10274,6 +15191,15 @@ form-data-encoder@1.7.1: resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== +form-data@^2.2.0, form-data@^2.3.3, form-data@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -10301,11 +15227,35 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formidable@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a" + integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -10313,7 +15263,7 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: +fresh@0.5.2, fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== @@ -10331,7 +15281,18 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.0.0: +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^10.0.0, fs-extra@^10.0.1: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -10358,7 +15319,7 @@ fs-extra@^4.0.2: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^7.0.1: +fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -10367,6 +15328,15 @@ fs-extra@^7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -10384,11 +15354,30 @@ fs-minipass@^1.2.7: dependencies: minipass "^2.6.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" + fs-monkey@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -10437,6 +15426,11 @@ function.prototype.name@^1.1.6: es-abstract "^1.22.1" functions-have-names "^1.2.3" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -10447,6 +15441,40 @@ futoin-hkdf@^1.5.3: resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.5.3.tgz#6c8024f2e1429da086d4e18289ef2239ad33ee35" integrity sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +gaxios@^6.0.0, gaxios@^6.0.3, gaxios@^6.1.1: + version "6.6.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.6.0.tgz#af8242fff0bbb82a682840d5feaa91b6a1c58be4" + integrity sha512-bpOZVQV5gthH/jVCSuYuokRo2bTKOcuBiVWpjmTn6C5Agl5zclGfTljuGsQZxwwDBkli+YhZhP4TdlqTnhOezQ== + dependencies: + extend "^3.0.2" + https-proxy-agent "^7.0.1" + is-stream "^2.0.0" + node-fetch "^2.6.9" + uuid "^9.0.1" + +gcp-metadata@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" + integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== + dependencies: + gaxios "^6.0.0" + json-bigint "^1.0.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -10483,6 +15511,11 @@ get-port-please@^3.1.2: resolved "https://registry.yarnpkg.com/get-port-please/-/get-port-please-3.1.2.tgz#502795e56217128e4183025c89a48c71652f4e49" integrity sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ== +get-port@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -10545,6 +15578,14 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +ghost-testrpc@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92" + integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ== + dependencies: + chalk "^2.4.2" + node-emoji "^1.10.0" + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -10560,7 +15601,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.2: +glob-parent@^6.0.1, glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -10572,6 +15613,46 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@9.3.2: version "9.3.2" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.2.tgz#8528522e003819e63d11c979b30896e0eaf52eda" @@ -10582,6 +15663,17 @@ glob@9.3.2: minipass "^4.2.4" path-scurry "^1.6.1" +glob@^10.2.2, glob@^10.3.10: + version "10.3.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.15.tgz#e72bc61bc3038c90605f5dd48543dc67aaf3b50d" + integrity sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.11.0" + glob@^10.3.3: version "10.3.10" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" @@ -10593,7 +15685,18 @@ glob@^10.3.3: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA== + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -10612,6 +15715,22 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + global@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" @@ -10639,6 +15758,20 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -10651,6 +15784,18 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^12.0.2: + version "12.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" + integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== + dependencies: + array-union "^3.0.1" + dir-glob "^3.0.1" + fast-glob "^3.2.7" + ignore "^5.1.9" + merge2 "^1.4.1" + slash "^4.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -10700,6 +15845,38 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +google-auth-library@^9.0.0, google-auth-library@^9.7.0: + version "9.10.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.10.0.tgz#c9fb940923f7ff2569d61982ee1748578c0bbfd4" + integrity sha512-ol+oSa5NbcGdDqA+gZ3G3mev59OHBZksBTxY/tYwjtcp1H/scAFwJfSQU9/1RALoyZ7FslNbke8j4i3ipwlyuQ== + dependencies: + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + gaxios "^6.1.1" + gcp-metadata "^6.1.0" + gtoken "^7.0.0" + jws "^4.0.0" + +googleapis-common@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/googleapis-common/-/googleapis-common-7.2.0.tgz#5c19102c9af1e5d27560be5e69ee2ccf68755d42" + integrity sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA== + dependencies: + extend "^3.0.2" + gaxios "^6.0.3" + google-auth-library "^9.7.0" + qs "^6.7.0" + url-template "^2.0.8" + uuid "^9.0.0" + +googleapis@^137.1.0: + version "137.1.0" + resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-137.1.0.tgz#f7896e6676e6e5ca358741bd7e837d8dfce94e96" + integrity sha512-2L7SzN0FLHyQtFmyIxrcXhgust77067pkkduqkbIpDuj9JzVnByxsRrcRfUMFQam3rQkWW2B0f1i40IwKDWIVQ== + dependencies: + google-auth-library "^9.0.0" + googleapis-common "^7.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -10743,7 +15920,7 @@ got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -10767,6 +15944,14 @@ graphql@^15.6.1: resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== +gtoken@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== + dependencies: + gaxios "^6.0.0" + jws "^4.0.0" + gzip-size@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" @@ -10795,6 +15980,18 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== +handlebars@^4.0.1: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -10808,6 +16005,71 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hardhat-gas-reporter@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz#ebe5bda5334b5def312747580cd923c2b09aef1b" + integrity sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA== + dependencies: + array-uniq "1.0.3" + eth-gas-reporter "^0.2.25" + sha1 "^1.1.1" + +hardhat@2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.14.0.tgz#b60c74861494aeb1b50803cf04cc47865a42b87a" + integrity sha512-73jsInY4zZahMSVFurSK+5TNCJTXMv+vemvGia0Ac34Mm19fYp6vEPVGF3sucbumszsYxiTT2TbS8Ii2dsDSoQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-blockchain" "7.0.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-evm" "2.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-statemanager" "2.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + "@nomicfoundation/ethereumjs-vm" "7.0.1" + "@nomicfoundation/solidity-analyzer" "^0.1.0" + "@sentry/node" "^5.18.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^1.0.3" + ethereumjs-abi "^0.6.8" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "7.2.0" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + keccak "^3.0.2" + lodash "^4.17.11" + mnemonist "^0.38.0" + mocha "^10.0.0" + p-map "^4.0.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + tsort "0.0.1" + undici "^5.14.0" + uuid "^8.3.2" + ws "^7.4.6" + harmony-reflect@^1.4.6: version "1.6.2" resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" @@ -10825,6 +16087,11 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -10859,6 +16126,11 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.1, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -10929,16 +16201,31 @@ hasown@^2.0.0, hasown@^2.0.1: dependencies: function-bind "^1.1.2" -he@1.2.x, he@^1.2.0: +he@1.2.0, he@1.2.x, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +"heap@>= 0.2.0": + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== + +helmet@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-7.1.0.tgz#287279e00f8a3763d5dccbaf1e5ee39b8c3784ca" + integrity sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg== + hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +hexoid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" + integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== + hey-listen@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" @@ -11014,6 +16301,11 @@ html-entities@^1.3.1: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== +html-entities@^2.1.0, html-entities@^2.3.2, html-entities@^2.3.6: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -11072,7 +16364,25 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0: +http-assert@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" + integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.8.0" + +http-basic@^8.1.1: + version "8.1.3" + resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" + integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== + dependencies: + caseless "^0.12.0" + concat-stream "^1.6.2" + http-response-object "^3.0.1" + parse-cache-control "^1.0.1" + +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -11093,6 +16403,17 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@^1.6.3, http-errors@~1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + http-errors@~1.6.2: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" @@ -11122,6 +16443,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -11143,6 +16472,17 @@ http-proxy-middleware@^1.0.0: is-plain-obj "^3.0.0" micromatch "^4.0.2" +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + http-proxy@^1.17.0, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -11152,6 +16492,13 @@ http-proxy@^1.17.0, http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-response-object@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" + integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== + dependencies: + "@types/node" "^10.0.3" + http-server@^14.1.0: version "14.1.1" resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e" @@ -11223,6 +16570,21 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" +https-proxy-agent@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + +human-interval@~2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/human-interval/-/human-interval-2.0.1.tgz#655baf606c7067bb26042dcae14ec777b099af15" + integrity sha512-r4Aotzf+OtKIGQCB3odUowy4GfUDTy3aTWTfLd7ZF2gBCy3XW3v/dJLRefZnOFFnjqs5B1TypvS8WarpBkYUNQ== + dependencies: + numbered "^1.1.0" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -11259,7 +16621,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.2: +iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -11273,6 +16635,11 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + idb-keyval@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.1.tgz#94516d625346d16f56f3b33855da11bfded2db33" @@ -11312,7 +16679,7 @@ ignore@^4.0.3: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4: +ignore@^5.0.4, ignore@^5.1.1, ignore@^5.1.9, ignore@^5.2.0, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -11322,11 +16689,21 @@ image-size@~0.5.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + immutable@^4.0.0: version "4.3.5" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0" integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== +immutable@^4.0.0-rc.12: + version "4.3.6" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.6.tgz#6a05f7858213238e587fb83586ffa3b4b27f0447" + integrity sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -11342,7 +16719,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.1.0, import-fresh@^3.2.1: +import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -11357,6 +16734,16 @@ import-from@^2.1.0: dependencies: resolve-from "^3.0.0" +import-in-the-middle@^1.6.0: + version "1.7.4" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz#508da6e91cfa84f210dcdb6c0a91ab0c9e8b3ebc" + integrity sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg== + dependencies: + acorn "^8.8.2" + acorn-import-attributes "^1.9.5" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -11411,12 +16798,12 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@2.0.0: +ini@2.0.0, ini@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.4: +ini@^1.3.4, ini@^1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -11438,6 +16825,11 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + invariant@2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -11445,6 +16837,13 @@ invariant@2.2.4: dependencies: loose-envify "^1.0.0" +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + ioredis@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.3.2.tgz#9139f596f62fc9c72d873353ac5395bcf05709f7" @@ -11460,11 +16859,24 @@ ioredis@^5.3.2: redis-parser "^3.0.0" standard-as-callback "^2.1.0" +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== +ip-regex@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + ip@^1.1.0, ip@^1.1.5: version "1.1.9" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" @@ -11475,6 +16887,11 @@ ipaddr.js@1.9.1, ipaddr.js@^1.9.0: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + iron-webcrypto@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-1.0.0.tgz#e3b689c0c61b434a0a4cb82d0aeabbc8b672a867" @@ -11557,6 +16974,11 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-buffer@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -11569,7 +16991,7 @@ is-ci@^1.0.10: dependencies: ci-info "^1.5.0" -is-ci@^3.0.0: +is-ci@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== @@ -11723,6 +17145,35 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-invalid-path@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-invalid-path/-/is-invalid-path-1.0.2.tgz#2f84731559f4936abcf1b227632719cf45c5dc0e" + integrity sha512-6KLcFrPCEP3AFXMfnWrIFkZpYNBVzZAoBJJDEZKtI3LXkaDjM3uFMJQjxiizUuZTZ9Oh9FNv/soXbx5TcpaDmA== + +is-ip@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" + integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== + dependencies: + ip-regex "^4.0.0" + +is-ipfs@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/is-ipfs/-/is-ipfs-0.6.3.tgz#82a5350e0a42d01441c40b369f8791e91404c497" + integrity sha512-HyRot1dvLcxImtDqPxAaY1miO6WsiP/z7Yxpg2qpaLWv5UdhAPtLvHJ4kMLM0w8GSl8AFsVF23PHe1LzuWrUlQ== + dependencies: + bs58 "^4.0.1" + cids "~0.7.0" + mafmt "^7.0.0" + multiaddr "^7.2.1" + multibase "~0.6.0" + multihashes "~0.4.13" + +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + is-nan@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" @@ -11940,6 +17391,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -11965,7 +17421,7 @@ isomorphic-timers-promises@^1.0.1: resolved "https://registry.yarnpkg.com/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz#e4137c24dbc54892de8abae3a4b5c1ffff381598" integrity sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ== -isomorphic-unfetch@3.1.0: +isomorphic-unfetch@3.1.0, isomorphic-unfetch@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== @@ -11978,7 +17434,7 @@ isows@1.0.3: resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== -isstream@~0.1.2: +isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== @@ -12045,7 +17501,7 @@ istanbul-reports@^3.1.3, istanbul-reports@^3.1.4, istanbul-reports@^3.1.6: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jackspeak@^2.3.5: +jackspeak@^2.3.5, jackspeak@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== @@ -12191,7 +17647,7 @@ jest-environment-jsdom@29.4.3: jest-util "^29.4.3" jsdom "^20.0.0" -jest-environment-node@^29.7.0: +jest-environment-node@^29.4.1, jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== @@ -12433,7 +17889,16 @@ jest-watcher@^29.7.0: jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^29.7.0: +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== @@ -12458,6 +17923,11 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== +jose@^4.10.3, jose@^4.14.6: + version "4.15.5" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.5.tgz#6475d0f467ecd3c630a1b5dadd2735a7288df706" + integrity sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg== + jose@^4.14.4: version "4.15.4" resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.4.tgz#02a9a763803e3872cf55f29ecef0dfdcc218cc03" @@ -12474,6 +17944,11 @@ js-beautify@^1.14.9, js-beautify@^1.6.12: js-cookie "^3.0.5" nopt "^7.2.0" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-cookie@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" @@ -12484,6 +17959,11 @@ js-message@1.0.7: resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47" integrity sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA== +js-sdsl@^4.1.4: + version "4.4.2" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.2.tgz#2e3c031b1f47d3aca8b775532e3ebb0818e7f847" + integrity sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w== + js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -12494,6 +17974,11 @@ js-sha3@^0.5.7: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== +js-sha3@^0.9.2, js-sha3@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.9.3.tgz#f0209432b23a66a0f6c7af592c26802291a75c2a" + integrity sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -12509,6 +17994,14 @@ js-tokens@^8.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-8.0.3.tgz#1c407ec905643603b38b6be6977300406ec48775" integrity sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw== +js-yaml@3.x, js-yaml@^3.10.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -12516,13 +18009,10 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.10.0, js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== jsbn@~0.1.0: version "0.1.1" @@ -12595,11 +18085,23 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -12610,7 +18112,7 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.3.0: +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== @@ -12642,6 +18144,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" @@ -12662,7 +18169,12 @@ json-stable-stringify@^1.0.2: jsonify "^0.0.1" object-keys "^1.1.1" -json-stringify-safe@~5.0.1: +json-stringify-deterministic@^1.0.7: + version "1.0.12" + resolved "https://registry.yarnpkg.com/json-stringify-deterministic/-/json-stringify-deterministic-1.0.12.tgz#aaa3f907466ed01e3afd77b898d0a2b3b132820a" + integrity sha512-q3PN0lbUdv0pmurkBNdJH3pfFvOTL/Zp0lquqpvcjfKzt6Y0j49EPHAmVHCAS4Ceq/Y+PejWTzyiVpoY71+D6g== + +json-stringify-safe@^5.0.0, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== @@ -12709,6 +18221,13 @@ jsonc-parser@^3.2.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -12730,6 +18249,27 @@ jsonify@^0.0.1: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== +jsonschema@^1.2.4, jsonschema@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + +jsonwebtoken@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -12740,22 +18280,88 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" -jsprim@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" - integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +jsrsasign@^10.5.25: + version "10.9.0" + resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.9.0.tgz#cc3f316e7e4c112a976193f9d2c93deb5a0745ee" + integrity sha512-QWLUikj1SBJGuyGK8tjKSx3K7Y69KYJnrs/pQ1KZ6wvZIkHkWjZ1PJDpuvc1/28c1uP0KW9qn1eI1LzHQqDOwQ== + +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwks-rsa@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-3.1.0.tgz#50406f23e38c9b2682cd437f824d7d61aa983171" + integrity sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg== + dependencies: + "@types/express" "^4.17.17" + "@types/jsonwebtoken" "^9.0.2" + debug "^4.3.4" + jose "^4.14.6" + limiter "^1.1.5" + lru-memoizer "^2.2.0" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" + jwa "^2.0.0" + safe-buffer "^5.0.1" jwt-decode@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -keccak@^3.0.0, keccak@^3.0.3: +kareem@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.6.3.tgz#23168ec8ffb6c1abfd31b7169a6fb1dd285992ac" + integrity sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q== + +keccak@^3.0.0, keccak@^3.0.2, keccak@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== @@ -12764,6 +18370,13 @@ keccak@^3.0.0, keccak@^3.0.3: node-gyp-build "^4.2.0" readable-stream "^3.6.0" +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + keyv@^4.0.0, keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -12800,11 +18413,70 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== + optionalDependencies: + graceful-fs "^4.1.9" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +klona@^2.0.4, klona@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +koa-compose@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-convert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" + integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== + dependencies: + co "^4.6.0" + koa-compose "^4.1.0" + +koa@^2.13.4: + version "2.15.3" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.15.3.tgz#062809266ee75ce0c75f6510a005b0e38f8c519a" + integrity sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.9.0" + debug "^4.3.2" + delegates "^1.0.0" + depd "^2.0.0" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^2.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + launch-editor-middleware@^2.2.1: version "2.6.1" resolved "https://registry.yarnpkg.com/launch-editor-middleware/-/launch-editor-middleware-2.6.1.tgz#7f2f400d8dda2283b69d02e9d83b1d272fef2bfb" @@ -12812,7 +18484,7 @@ launch-editor-middleware@^2.2.1: dependencies: launch-editor "^2.6.1" -launch-editor@^2.2.1, launch-editor@^2.6.1: +launch-editor@^2.2.1, launch-editor@^2.6.0, launch-editor@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== @@ -12825,6 +18497,13 @@ lazy-ass@^1.6.0: resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== +less-loader@11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-11.1.0.tgz#a452384259bdf8e4f6d5fdcc39543609e6313f82" + integrity sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug== + dependencies: + klona "^2.0.4" + less-loader@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-5.0.0.tgz#498dde3a6c6c4f887458ee9ed3f086a12ad1b466" @@ -12834,6 +18513,23 @@ less-loader@^5.0.0: loader-utils "^1.1.0" pify "^4.0.1" +less@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/less/-/less-4.1.3.tgz#175be9ddcbf9b250173e0a00b4d6920a5b770246" + integrity sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA== + dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" + tslib "^2.3.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + needle "^3.1.0" + source-map "~0.6.0" + less@^3.0.4: version "3.13.1" resolved "https://registry.yarnpkg.com/less/-/less-3.13.1.tgz#0ebc91d2a0e9c0c6735b83d496b0ab0583077909" @@ -12850,6 +18546,28 @@ less@^3.0.4: native-request "^1.0.5" source-map "~0.6.0" +level-supports@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== + +level-transcoder@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== + dependencies: + buffer "^6.0.3" + module-error "^1.0.1" + +level@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/level/-/level-8.0.1.tgz#737161db1bc317193aca4e7b6f436e7e1df64379" + integrity sha512-oPBGkheysuw7DmzFQYyFe8NAia5jFLAgEnkgWnK3OXAuJr8qFT+xBQIwokAZPME2bhPFzS8hlYcL16m8UZrtwQ== + dependencies: + abstract-level "^1.0.4" + browser-level "^1.0.1" + classic-level "^1.2.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -12863,6 +18581,43 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +libphonenumber-js@^1.10.53: + version "1.11.1" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz#2596683e1876bfee74082bb49339fe0a85ae34f9" + integrity sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw== + +license-webpack-plugin@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz#1e18442ed20b754b82f1adeff42249b81d11aec6" + integrity sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw== + dependencies: + webpack-sources "^3.0.0" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +lilconfig@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.1.tgz#9d8a246fa753106cfc205fd2d77042faca56e5e3" + integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ== + +limiter@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" + integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -12966,6 +18721,11 @@ loader-runner@^2.3.1, loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + loader-utils@^0.2.16: version "0.2.17" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" @@ -12985,7 +18745,7 @@ loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: emojis-list "^3.0.0" json5 "^1.0.1" -loader-utils@^2.0.0: +loader-utils@^2.0.0, loader-utils@^2.0.3, loader-utils@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== @@ -13007,6 +18767,14 @@ local-pkg@^0.5.0: mlly "^1.4.2" pkg-types "^1.0.3" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -13029,11 +18797,23 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + lodash-es@latest: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -13054,16 +18834,56 @@ lodash.defaultsdeep@^4.6.1: resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6" integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA== +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.groupby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + lodash.isarguments@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + lodash.isequal@4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + lodash.kebabcase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -13084,16 +18904,26 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.once@^4.1.1: +lodash.once@^4.0.0, lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== +lodash.snakecase@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + lodash.transform@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0" integrity sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ== +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -13104,14 +18934,7 @@ lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-symbols@^4.0.0: +log-symbols@4.1.0, log-symbols@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -13119,6 +18942,13 @@ log-symbols@^4.0.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-update@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" @@ -13129,12 +18959,35 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +log4js@^6.9.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + loglevel@^1.6.8, loglevel@^1.8.1: version "1.9.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.1.tgz#d63976ac9bcd03c7c873116d41c2a85bafff1be7" integrity sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg== -long@^5.0.0: +long@^5.0.0, long@^5.2.3: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== @@ -13158,6 +19011,13 @@ lower-case@^1.1.1: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" @@ -13168,11 +19028,23 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== +lru-cache@6.0.0, lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-cache@^10.0.1, lru-cache@^10.0.2, "lru-cache@^9.1.1 || ^10.0.0": version "10.2.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== +lru-cache@^10.2.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" + integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== + lru-cache@^4.0.1, lru-cache@^4.1.2: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -13188,12 +19060,42 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== +lru-memoizer@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.3.0.tgz#ef0fbc021bceb666794b145eefac6be49dc47f31" + integrity sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug== dependencies: - yallist "^4.0.0" + lodash.clonedeep "^4.5.0" + lru-cache "6.0.0" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + +lusca@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/lusca/-/lusca-1.7.0.tgz#a5d979f1b51776e60d41e0ca98f886f1b8b95502" + integrity sha512-msnrplCfY7zaqlZBDEloCIKld+RUeMZVeWzSPaGUKeRXFlruNSdKg2XxCyR+zj6BqzcXhXlRnvcvx6rAGgsvMA== + dependencies: + tsscmp "^1.0.5" + +luxon@^3, luxon@^3.2.1: + version "3.4.4" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" + integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== + +mafmt@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/mafmt/-/mafmt-7.1.0.tgz#4126f6d0eded070ace7dbbb6fb04977412d380b5" + integrity sha512-vpeo9S+hepT3k2h5iFxzEHvvR0GPBx9uKaErmnRzYNcaKb03DgOArjEMlgG4a9LcuZZ89a3I8xbeto487n26eA== + dependencies: + multiaddr "^7.3.0" + +magic-bytes.js@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz#c41cf4bc2f802992b05e64962411c9dd44fdef92" + integrity sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ== magic-string@0.27.0: version "0.27.0" @@ -13252,6 +19154,24 @@ make-error@1.x, make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +make-fetch-happen@^13.0.0: + version "13.0.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36" + integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA== + dependencies: + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" + is-lambda "^1.0.1" + minipass "^7.0.2" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + proc-log "^4.2.0" + promise-retry "^2.0.1" + ssri "^10.0.0" + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -13271,11 +19191,21 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-table@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" + integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== + marked@^12.0.2: version "12.0.2" resolved "https://registry.yarnpkg.com/marked/-/marked-12.0.2.tgz#b31578fe608b599944c69807b00f18edab84647e" integrity sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q== +mcl-wasm@^0.7.1: + version "0.7.9" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" + integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -13290,6 +19220,16 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" @@ -13300,7 +19240,7 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^3.1.2: +memfs@^3.1.2, memfs@^3.4.1, memfs@^3.4.3: version "3.6.0" resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== @@ -13323,6 +19263,25 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" +memory-level@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" + integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== + dependencies: + abstract-level "^1.0.0" + functional-red-black-tree "^1.0.1" + module-error "^1.0.1" + +memory-pager@^1.0.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" + integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -13352,7 +19311,7 @@ merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@~1.1.2: +methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== @@ -13394,6 +19353,19 @@ micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +migrate-mongo@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/migrate-mongo/-/migrate-mongo-11.0.0.tgz#d1b2291624fe8e134a0666ca77ad2fa18f42e337" + integrity sha512-GB/gHzUwp/fL1w6ksNGihTyb+cSrm6NbVLlz1OSkQKaLlzAXMwH7iKK2ZS7W5v+I8vXiY2rL58WTUZSAL6QR+A== + dependencies: + cli-table3 "^0.6.1" + commander "^9.1.0" + date-fns "^2.28.0" + fn-args "^5.0.0" + fs-extra "^10.0.1" + lodash "^4.17.21" + p-each-series "^2.2.0" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -13407,7 +19379,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -13419,7 +19391,7 @@ mime@1.6.0, mime@^1.4.1, mime@^1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.4: +mime@2.6.0, mime@^2.4.4: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -13449,6 +19421,11 @@ mimic-response@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" @@ -13471,6 +19448,13 @@ mini-css-extract-plugin@^0.9.0: schema-utils "^1.0.0" webpack-sources "^1.1.0" +mini-css-extract-plugin@~2.4.7: + version "2.4.7" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz#b9f4c4f4d727c7a3cd52a11773bb739f00177fac" + integrity sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg== + dependencies: + schema-utils "^4.0.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -13481,6 +19465,20 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + minimatch@9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" @@ -13495,13 +19493,6 @@ minimatch@9.0.3, minimatch@^9.0.1, minimatch@^9.0.3: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - minimatch@^5.0.1: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" @@ -13516,11 +19507,57 @@ minimatch@^7.4.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== + dependencies: + minipass "^7.0.3" + +minipass-fetch@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz#f0f97e40580affc4a35cc4a1349f05ae36cb1e4c" + integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -13529,7 +19566,7 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.1.1: +minipass@^3.0.0, minipass@^3.1.1: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -13541,11 +19578,21 @@ minipass@^4.2.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": version "7.0.4" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4: + version "7.1.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.1.tgz#f7f85aff59aa22f110b20e27692465cf3bf89481" + integrity sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -13553,6 +19600,14 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" +minizlib@^2.1.1, minizlib@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mipd@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mipd/-/mipd-0.0.5.tgz#367ee796531c23f0631f129038700b1406663aec" @@ -13589,6 +19644,11 @@ mixpanel-browser@^2.45.0: resolved "https://registry.yarnpkg.com/mixpanel-browser/-/mixpanel-browser-2.49.0.tgz#de3f4f2d0f3a32b4babf6d827ef983a9fd48a711" integrity sha512-RZJCO7XXuuHBAWG5fd9Mavz994M7v7W3Qiaq8NzmN631pa4BQ0vNZQtRFqKcCCOBn4xqOZbX2GkuC7ZkQoL4cQ== +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" @@ -13601,14 +19661,14 @@ mkdirp@*: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1: +mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" -mkdirp@~1.0.4: +mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -13618,25 +19678,160 @@ mlly@^1.2.0, mlly@^1.5.0: resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.6.0.tgz#0ecfbddc706857f5e170ccd28c6b0b9c81d3f548" integrity sha512-YOvg9hfYQmnaB56Yb+KrJE2u0Yzz5zR+sLejEvF4fzwzV1Al6hkf2vyHTwqCRyv0hCi9rVCqVoXpyYevQIRwLQ== dependencies: - acorn "^8.11.3" - pathe "^1.1.2" - pkg-types "^1.0.3" - ufo "^1.3.2" + acorn "^8.11.3" + pathe "^1.1.2" + pkg-types "^1.0.3" + ufo "^1.3.2" + +mlly@^1.4.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.6.1.tgz#0983067dc3366d6314fc5e12712884e6978d028f" + integrity sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA== + dependencies: + acorn "^8.11.3" + pathe "^1.1.2" + pkg-types "^1.0.3" + ufo "^1.3.2" + +mnemonist@^0.38.0: + version "0.38.5" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" + integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== + dependencies: + obliterator "^2.0.0" + +mocha@^10.0.0, mocha@^10.2.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.4.0.tgz#ed03db96ee9cfc6d20c56f8e2af07b961dbae261" + integrity sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "8.1.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + +module-error@^1.0.1, module-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" + integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== + +moment-timezone@^0.5.13: + version "0.5.45" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" + integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== + dependencies: + moment "^2.29.4" + +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +mongodb-connection-string-url@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf" + integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ== + dependencies: + "@types/whatwg-url" "^8.2.1" + whatwg-url "^11.0.0" + +mongodb-connection-string-url@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz#c13e6ac284ae401752ebafdb8cd7f16c6723b141" + integrity sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg== + dependencies: + "@types/whatwg-url" "^11.0.2" + whatwg-url "^13.0.0" + +mongodb@6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.5.0.tgz#3735b4fba085b26ca06f7744e9639bc538e93d87" + integrity sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA== + dependencies: + "@mongodb-js/saslprep" "^1.1.5" + bson "^6.4.0" + mongodb-connection-string-url "^3.0.0" + +mongodb@^4: + version "4.17.2" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.17.2.tgz#237c0534e36a3449bd74c6bf6d32f87a1ca7200c" + integrity sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg== + dependencies: + bson "^4.7.2" + mongodb-connection-string-url "^2.6.0" + socks "^2.7.1" + optionalDependencies: + "@aws-sdk/credential-providers" "^3.186.0" + "@mongodb-js/saslprep" "^1.1.0" + +mongodb@^6.1.0: + version "6.6.2" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.6.2.tgz#7ecdd788e9162f6c5726cef40bdd2813cc01e56c" + integrity sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw== + dependencies: + "@mongodb-js/saslprep" "^1.1.5" + bson "^6.7.0" + mongodb-connection-string-url "^3.0.0" + +mongoose@^8.3.5: + version "8.3.5" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-8.3.5.tgz#8eb81ecd4c2bda03678e73874594c777512e9973" + integrity sha512-2zqeAjHjCqT1o5HeUCvkE9tUHsXwemnwEZ2SKnUxsaP8p1a+UcSQSNbnSuOzUVePMwLETrsvLIRdFLjsNfCgWA== + dependencies: + bson "^6.5.0" + kareem "2.6.3" + mongodb "6.5.0" + mpath "0.9.0" + mquery "5.0.0" + ms "2.1.3" + sift "17.1.3" -mlly@^1.4.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.6.1.tgz#0983067dc3366d6314fc5e12712884e6978d028f" - integrity sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA== +morgan-body@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/morgan-body/-/morgan-body-2.6.9.tgz#92a864c66bef644019572a6611ef702fa42f2bad" + integrity sha512-O0dlv/V67gSszFo4rraZ7nMhD+iuWOg2w7hOLcAC6S+nF26Q8XvikGFjoJ7+dVuh49BBsIvQqRUGbs9e8keFKw== dependencies: - acorn "^8.11.3" - pathe "^1.1.2" - pkg-types "^1.0.3" - ufo "^1.3.2" + es6-symbol "^3.1.3" + moment-timezone "^0.5.13" + on-finished "^2.3.0" + on-headers "^1.0.1" -mock-fs@^4.1.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" - integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== +morgan@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" motion@10.16.2: version "10.16.2" @@ -13662,6 +19857,18 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +mpath@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" + integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew== + +mquery@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d" + integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg== + dependencies: + debug "4.x" + mri@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -13687,6 +19894,31 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multer@^1.4.5-lts.1: + version "1.4.5-lts.1" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" + integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== + dependencies: + append-field "^1.0.0" + busboy "^1.0.0" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + type-is "^1.6.4" + xtend "^4.0.0" + +multiaddr@^7.2.1, multiaddr@^7.3.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/multiaddr/-/multiaddr-7.5.0.tgz#976c88e256e512263445ab03b3b68c003d5f485e" + integrity sha512-GvhHsIGDULh06jyb6ev+VfREH9evJCFIRnh3jUt9iEZ6XDbyoisZRFEI9bMvK/AiR6y66y6P+eoBw9mBYMhMvw== + dependencies: + buffer "^5.5.0" + cids "~0.8.0" + class-is "^1.1.0" + is-ip "^3.1.0" + multibase "^0.7.0" + varint "^5.0.0" + multibase@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" @@ -13695,6 +19927,14 @@ multibase@^0.7.0: base-x "^3.0.8" buffer "^5.5.0" +multibase@^1.0.0, multibase@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-1.0.1.tgz#4adbe1de0be8a1ab0274328b653c3f1903476724" + integrity sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + multibase@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" @@ -13716,6 +19956,14 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + multicodec@^0.5.5: version "0.5.7" resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" @@ -13723,7 +19971,7 @@ multicodec@^0.5.5: dependencies: varint "^5.0.0" -multicodec@^1.0.0: +multicodec@^1.0.0, multicodec@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== @@ -13736,7 +19984,7 @@ multiformats@^9.4.2: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== -multihashes@^0.4.15, multihashes@~0.4.15: +multihashes@^0.4.15, multihashes@~0.4.13, multihashes@~0.4.15: version "0.4.21" resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== @@ -13745,6 +19993,15 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" +multihashes@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-1.0.1.tgz#a89415d68283cf6287c6e219e304e75ce7fb73fe" + integrity sha512-S27Tepg4i8atNiFaU5ZOm3+gl3KQlUanLs/jWcBxQHFttgq+5x1OgbQmf2d8axJ/48zYGBd/wT9d723USMFduw== + dependencies: + buffer "^5.6.0" + multibase "^1.0.1" + varint "^5.0.0" + mz@^2.4.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" @@ -13759,12 +20016,26 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== +nan@^2.15.0, nan@^2.17.0, nan@^2.18.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0" + integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw== + nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== -nanoid@^3.3.7: +nano@^10.1.2: + version "10.1.3" + resolved "https://registry.yarnpkg.com/nano/-/nano-10.1.3.tgz#5cb1ad14add4c9c82d53a79159848dafa84e7a13" + integrity sha512-q/hKQJJH3FhkkuJ3ojbgDph2StlSXFBPNkpZBZlsvZDbuYfxKJ4VtunEeilthcZtuIplIk1zVX5o2RgKTUTO+Q== + dependencies: + axios "^1.6.2" + node-abort-controller "^3.0.1" + qs "^6.11.0" + +nanoid@^3.3.4, nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== @@ -13786,6 +20057,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-macros@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.2.2.tgz#817fef20c3e0e40a963fbf7b37d1600bd0201044" + integrity sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g== + napi-wasm@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/napi-wasm/-/napi-wasm-1.1.0.tgz#bbe617823765ae9c1bc12ff5942370eae7b2ba4e" @@ -13801,16 +20077,58 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@0.6.3: +nconf@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/nconf/-/nconf-0.12.1.tgz#b0b91d2e32dc588fad19ac8bc5245e7472ebb4b9" + integrity sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w== + dependencies: + async "^3.0.0" + ini "^2.0.0" + secure-keys "^1.0.0" + yargs "^16.1.1" + +needle@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-3.3.1.tgz#63f75aec580c2e77e209f3f324e2cdf3d29bd049" + integrity sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q== + dependencies: + iconv-lite "^0.6.3" + sax "^1.2.4" + +negotiator@0.6.3, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +newrelic@^11.17.0: + version "11.17.0" + resolved "https://registry.yarnpkg.com/newrelic/-/newrelic-11.17.0.tgz#10dcf25c162968235c4d4ab764b3dd4833b34599" + integrity sha512-gI5FGsfvHyGLUW/+q3op1SsF8jisW5wV+NVOoxV9J58GOEEsP1B4/9D5jyL3iiL5QO1REeWtK5n15d2OsiYAIg== + dependencies: + "@grpc/grpc-js" "^1.9.4" + "@grpc/proto-loader" "^0.7.5" + "@newrelic/ritm" "^7.2.0" + "@newrelic/security-agent" "^1.1.1" + "@tyriar/fibonacci-heap" "^2.0.7" + concat-stream "^2.0.0" + https-proxy-agent "^7.0.1" + import-in-the-middle "^1.6.0" + json-bigint "^1.0.0" + json-stringify-safe "^5.0.0" + module-details-from-path "^1.0.3" + readable-stream "^3.6.1" + semver "^7.5.2" + winston-transport "^4.5.0" + optionalDependencies: + "@contrast/fn-inspect" "^3.3.0" + "@newrelic/native-metrics" "^10.0.0" + "@prisma/prisma-fmt-wasm" "^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" + next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -13828,6 +20146,35 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +nock@^13.5.4: + version "13.5.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" + integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + propagate "^2.0.0" + +node-abi@^3.3.0: + version "3.62.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.62.0.tgz#017958ed120f89a3a14a7253da810f5d724e3f36" + integrity sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g== + dependencies: + semver "^7.3.5" + +node-abort-controller@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -13843,6 +20190,13 @@ node-addon-api@^7.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + node-fetch-native@^1.4.0, node-fetch-native@^1.4.1, node-fetch-native@^1.6.1: version "1.6.2" resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.2.tgz#f439000d972eb0c8a741b65dcda412322955e1c6" @@ -13856,7 +20210,7 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.6, node-fetch@^2.6.7: +node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.6, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -13868,7 +20222,7 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-forge@^1.3.1: +node-forge@^1, node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== @@ -13878,6 +20232,27 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== +node-gyp-build@^4.6.0, node-gyp-build@^4.8.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" + integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== + +node-gyp@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.1.0.tgz#75e6f223f2acb4026866c26a2ead6aab75a8ca7e" + integrity sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^10.3.10" + graceful-fs "^4.2.6" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^3.0.0" + semver "^7.3.5" + tar "^6.1.2" + which "^4.0.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -13955,6 +20330,32 @@ node-stdlib-browser@^1.2.0: util "^0.12.4" vm-browserify "^1.0.1" +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + +nopt@3.x: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg== + dependencies: + abbrev "1" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== + dependencies: + abbrev "^2.0.0" + nopt@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" @@ -14031,6 +20432,13 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-3.1.0.tgz#7f91be317f6a466efed3c9f2980ad8a4ee8b0fa5" + integrity sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg== + dependencies: + path-key "^3.0.0" + npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -14045,6 +20453,16 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -14072,17 +20490,73 @@ number-to-bn@1.7.0: bn.js "4.11.6" strip-hex-prefix "1.0.0" +numbered@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/numbered/-/numbered-1.1.0.tgz#9fcd79564c73a84b9574e8370c3d8e58fe3c133c" + integrity sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g== + nwsapi@^2.2.2, nwsapi@^2.2.4: version "2.2.7" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== -nx@18.0.8: - version "18.0.8" - resolved "https://registry.yarnpkg.com/nx/-/nx-18.0.8.tgz#5d0ac8b53663cc53045c63005ff3fc7592a0bc5d" - integrity sha512-IhzRLCZaiR9zKGJ3Jm79bhi8nOdyRORQkFc/YDO6xubLSQ5mLPAeg789Q/SlGRzU5oMwLhm5D/gvvMJCAvUmXQ== +nx@18.3.0: + version "18.3.0" + resolved "https://registry.yarnpkg.com/nx/-/nx-18.3.0.tgz#3102a74057d19b561c57ee34a12f51b99e6fe376" + integrity sha512-0jIxAuRVW19uVP0xPcr9obk8YSQzh2E5Co/4AYvfuGlQegiRv/CYk5NDK3wzAe3l1rTSUhmbol7QxpZGXhk4Dw== dependencies: - "@nrwl/tao" "18.0.8" + "@nrwl/tao" "18.3.0" + "@yarnpkg/lockfile" "^1.1.0" + "@yarnpkg/parsers" "3.0.0-rc.46" + "@zkochan/js-yaml" "0.0.6" + axios "^1.6.0" + chalk "^4.1.0" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + cliui "^8.0.1" + dotenv "~16.3.1" + dotenv-expand "~10.0.0" + enquirer "~2.3.6" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + ignore "^5.0.4" + jest-diff "^29.4.1" + js-yaml "4.1.0" + jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" + minimatch "9.0.3" + node-machine-id "1.1.12" + npm-run-path "^4.0.1" + open "^8.4.0" + ora "5.3.0" + semver "^7.5.3" + string-width "^4.2.3" + strong-log-transformer "^2.1.0" + tar-stream "~2.2.0" + tmp "~0.2.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + optionalDependencies: + "@nx/nx-darwin-arm64" "18.3.0" + "@nx/nx-darwin-x64" "18.3.0" + "@nx/nx-freebsd-x64" "18.3.0" + "@nx/nx-linux-arm-gnueabihf" "18.3.0" + "@nx/nx-linux-arm64-gnu" "18.3.0" + "@nx/nx-linux-arm64-musl" "18.3.0" + "@nx/nx-linux-x64-gnu" "18.3.0" + "@nx/nx-linux-x64-musl" "18.3.0" + "@nx/nx-win32-arm64-msvc" "18.3.0" + "@nx/nx-win32-x64-msvc" "18.3.0" + +nx@19.0.4: + version "19.0.4" + resolved "https://registry.yarnpkg.com/nx/-/nx-19.0.4.tgz#c39803f6186f6b009c39f5f30f902ce8e136dcde" + integrity sha512-E+wkP3H+23Vu9jso6Xw7cbXPzy2PMyrPukrEUDWkQrr/eCqf0Npkj5zky1/lKFSBaLtNYgsFD21co+b4rwxtdw== + dependencies: + "@nrwl/tao" "19.0.4" "@yarnpkg/lockfile" "^1.1.0" "@yarnpkg/parsers" "3.0.0-rc.46" "@zkochan/js-yaml" "0.0.6" @@ -14117,16 +20591,16 @@ nx@18.0.8: yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nx/nx-darwin-arm64" "18.0.8" - "@nx/nx-darwin-x64" "18.0.8" - "@nx/nx-freebsd-x64" "18.0.8" - "@nx/nx-linux-arm-gnueabihf" "18.0.8" - "@nx/nx-linux-arm64-gnu" "18.0.8" - "@nx/nx-linux-arm64-musl" "18.0.8" - "@nx/nx-linux-x64-gnu" "18.0.8" - "@nx/nx-linux-x64-musl" "18.0.8" - "@nx/nx-win32-arm64-msvc" "18.0.8" - "@nx/nx-win32-x64-msvc" "18.0.8" + "@nx/nx-darwin-arm64" "19.0.4" + "@nx/nx-darwin-x64" "19.0.4" + "@nx/nx-freebsd-x64" "19.0.4" + "@nx/nx-linux-arm-gnueabihf" "19.0.4" + "@nx/nx-linux-arm64-gnu" "19.0.4" + "@nx/nx-linux-arm64-musl" "19.0.4" + "@nx/nx-linux-x64-gnu" "19.0.4" + "@nx/nx-linux-x64-musl" "19.0.4" + "@nx/nx-win32-arm64-msvc" "19.0.4" + "@nx/nx-win32-x64-msvc" "19.0.4" oauth-sign@~0.9.0: version "0.9.0" @@ -14156,6 +20630,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" @@ -14238,6 +20717,11 @@ object.values@^1.1.0, object.values@^1.1.7: define-properties "^1.2.0" es-abstract "^1.22.1" +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + oblivious-set@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.1.1.tgz#d9d38e9491d51f27a5c3ec1681d2ba40aa81e98b" @@ -14277,30 +20761,72 @@ oidc-client-ts@^2.2.4: crypto-js "^4.2.0" jwt-decode "^3.1.2" +oidc-provider@7.14.3: + version "7.14.3" + resolved "https://registry.yarnpkg.com/oidc-provider/-/oidc-provider-7.14.3.tgz#8d05c60c1cb839cb25320f85565f82472a689bce" + integrity sha512-+TB/JSB6jXyr5Ti2bqOSwDKI6ldc23XlHkgQTFUIjFm2P6SuTkmcXpvF3KF8kDWreSxlKwXwh7ubn9+9yR7CGA== + dependencies: + "@koa/cors" "^3.3.0" + cacheable-lookup "^6.0.4" + debug "^4.3.4" + ejs "^3.1.8" + got "^11.8.5" + jose "^4.10.3" + jsesc "^3.0.2" + koa "^2.13.4" + koa-compose "^4.1.0" + nanoid "^3.3.4" + object-hash "^3.0.0" + oidc-token-hash "^5.0.1" + paseto "^2.1.3" + quick-lru "^5.1.1" + raw-body "^2.5.1" + optionalDependencies: + paseto3 "npm:paseto@^3.1.0" + +oidc-token-hash@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz#9a229f0a1ce9d4fc89bcaee5478c97a889e7b7b6" + integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw== + on-exit-leak-free@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" -on-headers@~1.0.2: +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@^1.0.1, on-headers@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -14322,6 +20848,11 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== + open@^6.3.0: version "6.4.0" resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" @@ -14329,7 +20860,7 @@ open@^6.3.0: dependencies: is-wsl "^1.1.0" -open@^8.4.0: +open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== @@ -14338,6 +20869,13 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +openapi3-ts@^3.0.0, openapi3-ts@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-3.2.0.tgz#7e30d33c480e938e67e809ab16f419bc9beae3f8" + integrity sha512-/ykNWRV5Qs0Nwq7Pc0nJ78fgILvOT/60OxEmB3v7yQ8a8Bwcm43D4diaYazG/KBn6czA+52XYy931WFLMCUeSg== + dependencies: + yaml "^2.2.1" + opener@^1.5.1: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -14350,6 +20888,18 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -14388,11 +20938,21 @@ ora@^3.4.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" +ordinal@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d" + integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ== + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + ospath@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" @@ -14408,6 +20968,11 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== +p-each-series@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -14418,6 +20983,13 @@ p-finally@^2.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -14432,6 +21004,13 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + p-limit@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" @@ -14439,6 +21018,13 @@ p-limit@^5.0.0: dependencies: yocto-queue "^1.0.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== + dependencies: + p-limit "^1.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -14460,6 +21046,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -14479,12 +21072,25 @@ p-retry@^3.0.1: dependencies: retry "^0.12.0" +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pako@~1.0.5: +pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -14523,6 +21129,11 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.6: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-cache-control@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== + parse-headers@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" @@ -14546,6 +21157,11 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse5-htmlparser2-tree-adapter@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" @@ -14553,6 +21169,11 @@ parse5-htmlparser2-tree-adapter@^6.0.0: dependencies: parse5 "^6.0.1" +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + parse5@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" @@ -14570,7 +21191,7 @@ parse5@^7.0.0, parse5@^7.1.1, parse5@^7.1.2: dependencies: entities "^4.4.0" -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@^1.3.2, parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -14580,6 +21201,16 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== +"paseto3@npm:paseto@^3.1.0": + version "3.1.4" + resolved "https://registry.yarnpkg.com/paseto/-/paseto-3.1.4.tgz#a8a448abadb20fef609e00a4fd6d82369b3ea05a" + integrity sha512-BifaKKu+MS9b/vTgFMC6Q8uLUMqw8VtYgl4qODJWb6Jqt+dTKn8XH9EftJZx+6wxF4ELBbKdH33DZa4inMYVcg== + +paseto@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/paseto/-/paseto-2.1.3.tgz#9913f9f46172ce88c397a0f035fdfedd34d827ef" + integrity sha512-BNkbvr0ZFDbh3oV13QzT5jXIu8xpFc9r0o5mvWBhDU1GBkVt1IzHK1N6dcYmN7XImrUmPQ0HCUXmoe2WPo8xsg== + path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" @@ -14605,6 +21236,11 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -14630,7 +21266,7 @@ path-key@^4.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== -path-parse@^1.0.7: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -14643,6 +21279,14 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^1.11.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -14660,6 +21304,14 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== + dependencies: + process "^0.11.1" + util "^0.10.3" + pathe@^1.1.0, pathe@^1.1.1, pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -14706,7 +21358,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.0.0, pify@^2.2.0: +pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== @@ -14781,6 +21433,13 @@ pirates@^4.0.4, pirates@^4.0.6: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +pkcs11js@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/pkcs11js/-/pkcs11js-1.3.1.tgz#2eec2c712594c088e271f04b363252d4c3df7778" + integrity sha512-eo7fTeQwYGzX1pFmRaf4ji/WcDW2XKpwqylOwzutsjNWECv6G9PzDHj3Yj5dX9EW/fydMnJG8xvWj/btnQT9TA== + dependencies: + nan "^2.15.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -14802,6 +21461,13 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" +pkg-dir@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" + integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== + dependencies: + find-up "^6.3.0" + pkg-types@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" @@ -14870,6 +21536,14 @@ postcss-calc@^7.0.1: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.0.2" +postcss-calc@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6" + integrity sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ== + dependencies: + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + postcss-colormin@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" @@ -14881,6 +21555,16 @@ postcss-colormin@^4.0.3: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-colormin@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-6.1.0.tgz#076e8d3fb291fbff7b10e6b063be9da42ff6488d" + integrity sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw== + dependencies: + browserslist "^4.23.0" + caniuse-api "^3.0.0" + colord "^2.9.3" + postcss-value-parser "^4.2.0" + postcss-convert-values@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" @@ -14889,6 +21573,14 @@ postcss-convert-values@^4.0.1: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-convert-values@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz#3498387f8efedb817cbc63901d45bd1ceaa40f48" + integrity sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w== + dependencies: + browserslist "^4.23.0" + postcss-value-parser "^4.2.0" + postcss-discard-comments@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" @@ -14896,6 +21588,11 @@ postcss-discard-comments@^4.0.2: dependencies: postcss "^7.0.0" +postcss-discard-comments@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz#e768dcfdc33e0216380623652b0a4f69f4678b6c" + integrity sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw== + postcss-discard-duplicates@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" @@ -14903,6 +21600,11 @@ postcss-discard-duplicates@^4.0.2: dependencies: postcss "^7.0.0" +postcss-discard-duplicates@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz#d121e893c38dc58a67277f75bb58ba43fce4c3eb" + integrity sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw== + postcss-discard-empty@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" @@ -14910,6 +21612,11 @@ postcss-discard-empty@^4.0.1: dependencies: postcss "^7.0.0" +postcss-discard-empty@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz#ee39c327219bb70473a066f772621f81435a79d9" + integrity sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ== + postcss-discard-overridden@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" @@ -14917,6 +21624,20 @@ postcss-discard-overridden@^4.0.1: dependencies: postcss "^7.0.0" +postcss-discard-overridden@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz#4e9f9c62ecd2df46e8fdb44dc17e189776572e2d" + integrity sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ== + +postcss-import@~14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" + integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + postcss-load-config@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" @@ -14935,6 +21656,15 @@ postcss-loader@^3.0.0: postcss-load-config "^2.0.0" schema-utils "^1.0.0" +postcss-loader@^6.1.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" + integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.5" + postcss-merge-longhand@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" @@ -14945,6 +21675,14 @@ postcss-merge-longhand@^4.0.11: postcss-value-parser "^3.0.0" stylehacks "^4.0.0" +postcss-merge-longhand@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz#ba8a8d473617c34a36abbea8dda2b215750a065a" + integrity sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^6.1.1" + postcss-merge-rules@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" @@ -14957,6 +21695,16 @@ postcss-merge-rules@^4.0.3: postcss-selector-parser "^3.0.0" vendors "^1.0.0" +postcss-merge-rules@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz#7aa539dceddab56019469c0edd7d22b64c3dea9d" + integrity sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ== + dependencies: + browserslist "^4.23.0" + caniuse-api "^3.0.0" + cssnano-utils "^4.0.2" + postcss-selector-parser "^6.0.16" + postcss-minify-font-values@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" @@ -14965,6 +21713,13 @@ postcss-minify-font-values@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-minify-font-values@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz#a0e574c02ee3f299be2846369211f3b957ea4c59" + integrity sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg== + dependencies: + postcss-value-parser "^4.2.0" + postcss-minify-gradients@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" @@ -14975,6 +21730,15 @@ postcss-minify-gradients@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-minify-gradients@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz#ca3eb55a7bdb48a1e187a55c6377be918743dbd6" + integrity sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q== + dependencies: + colord "^2.9.3" + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + postcss-minify-params@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" @@ -14987,6 +21751,15 @@ postcss-minify-params@^4.0.2: postcss-value-parser "^3.0.0" uniqs "^2.0.0" +postcss-minify-params@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz#54551dec77b9a45a29c3cb5953bf7325a399ba08" + integrity sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA== + dependencies: + browserslist "^4.23.0" + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + postcss-minify-selectors@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" @@ -14997,6 +21770,13 @@ postcss-minify-selectors@^4.0.2: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +postcss-minify-selectors@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz#197f7d72e6dd19eed47916d575d69dc38b396aff" + integrity sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ== + dependencies: + postcss-selector-parser "^6.0.16" + postcss-modules-extract-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" @@ -15004,6 +21784,11 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + postcss-modules-local-by-default@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" @@ -15014,6 +21799,15 @@ postcss-modules-local-by-default@^3.0.2: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" +postcss-modules-local-by-default@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + postcss-modules-scope@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" @@ -15022,6 +21816,13 @@ postcss-modules-scope@^2.2.0: postcss "^7.0.6" postcss-selector-parser "^6.0.0" +postcss-modules-scope@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== + dependencies: + postcss-selector-parser "^6.0.4" + postcss-modules-values@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" @@ -15030,6 +21831,13 @@ postcss-modules-values@^3.0.0: icss-utils "^4.0.0" postcss "^7.0.6" +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + postcss-normalize-charset@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" @@ -15037,6 +21845,11 @@ postcss-normalize-charset@^4.0.1: dependencies: postcss "^7.0.0" +postcss-normalize-charset@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz#1ec25c435057a8001dac942942a95ffe66f721e1" + integrity sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ== + postcss-normalize-display-values@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" @@ -15046,6 +21859,13 @@ postcss-normalize-display-values@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-display-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz#54f02764fed0b288d5363cbb140d6950dbbdd535" + integrity sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg== + dependencies: + postcss-value-parser "^4.2.0" + postcss-normalize-positions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" @@ -15056,6 +21876,13 @@ postcss-normalize-positions@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-positions@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz#e982d284ec878b9b819796266f640852dbbb723a" + integrity sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q== + dependencies: + postcss-value-parser "^4.2.0" + postcss-normalize-repeat-style@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" @@ -15066,6 +21893,13 @@ postcss-normalize-repeat-style@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-repeat-style@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz#f8006942fd0617c73f049dd8b6201c3a3040ecf3" + integrity sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ== + dependencies: + postcss-value-parser "^4.2.0" + postcss-normalize-string@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" @@ -15075,6 +21909,13 @@ postcss-normalize-string@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-string@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz#e3cc6ad5c95581acd1fc8774b309dd7c06e5e363" + integrity sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ== + dependencies: + postcss-value-parser "^4.2.0" + postcss-normalize-timing-functions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" @@ -15084,6 +21925,13 @@ postcss-normalize-timing-functions@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-timing-functions@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz#40cb8726cef999de984527cbd9d1db1f3e9062c0" + integrity sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA== + dependencies: + postcss-value-parser "^4.2.0" + postcss-normalize-unicode@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" @@ -15093,6 +21941,14 @@ postcss-normalize-unicode@^4.0.1: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-unicode@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz#aaf8bbd34c306e230777e80f7f12a4b7d27ce06e" + integrity sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg== + dependencies: + browserslist "^4.23.0" + postcss-value-parser "^4.2.0" + postcss-normalize-url@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" @@ -15103,6 +21959,13 @@ postcss-normalize-url@^4.0.1: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-url@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz#292792386be51a8de9a454cb7b5c58ae22db0f79" + integrity sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ== + dependencies: + postcss-value-parser "^4.2.0" + postcss-normalize-whitespace@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" @@ -15111,6 +21974,13 @@ postcss-normalize-whitespace@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-normalize-whitespace@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz#fbb009e6ebd312f8b2efb225c2fcc7cf32b400cd" + integrity sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q== + dependencies: + postcss-value-parser "^4.2.0" + postcss-ordered-values@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" @@ -15120,6 +21990,14 @@ postcss-ordered-values@^4.1.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-ordered-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz#366bb663919707093451ab70c3f99c05672aaae5" + integrity sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q== + dependencies: + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + postcss-reduce-initial@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" @@ -15130,6 +22008,14 @@ postcss-reduce-initial@^4.0.3: has "^1.0.0" postcss "^7.0.0" +postcss-reduce-initial@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz#4401297d8e35cb6e92c8e9586963e267105586ba" + integrity sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw== + dependencies: + browserslist "^4.23.0" + caniuse-api "^3.0.0" + postcss-reduce-transforms@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" @@ -15140,6 +22026,13 @@ postcss-reduce-transforms@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-reduce-transforms@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz#6fa2c586bdc091a7373caeee4be75a0f3e12965d" + integrity sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA== + dependencies: + postcss-value-parser "^4.2.0" + postcss-selector-parser@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" @@ -15157,6 +22050,14 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16, postcss-selector-parser@^6.0.4: + version "6.0.16" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz#3b88b9f5c5abd989ef4e2fc9ec8eedd34b20fb04" + integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" @@ -15166,6 +22067,14 @@ postcss-svgo@^4.0.3: postcss-value-parser "^3.0.0" svgo "^1.0.0" +postcss-svgo@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.3.tgz#1d6e180d6df1fa8a3b30b729aaa9161e94f04eaa" + integrity sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^3.2.0" + postcss-unique-selectors@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" @@ -15175,12 +22084,19 @@ postcss-unique-selectors@^4.0.1: postcss "^7.0.0" uniqs "^2.0.0" +postcss-unique-selectors@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz#983ab308896b4bf3f2baaf2336e14e52c11a2088" + integrity sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg== + dependencies: + postcss-selector-parser "^6.0.16" + postcss-value-parser@^3.0.0: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== @@ -15193,6 +22109,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.3 picocolors "^0.2.1" source-map "^0.6.1" +postcss@^8.4.14, postcss@^8.4.24: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + postcss@^8.4.33, postcss@^8.4.35: version "8.4.35" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7" @@ -15207,11 +22132,28 @@ preact@^10.16.0: resolved "https://registry.yarnpkg.com/preact/-/preact-10.19.6.tgz#66007b67aad4d11899f583df1b0116d94a89b8f5" integrity sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw== +prebuildify@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/prebuildify/-/prebuildify-6.0.1.tgz#655746f91fc95b68610615898678536dd303cd03" + integrity sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw== + dependencies: + minimist "^1.2.5" + mkdirp-classic "^0.5.3" + node-abi "^3.3.0" + npm-run-path "^3.1.0" + pump "^3.0.0" + tar-fs "^2.1.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -15224,7 +22166,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -"prettier@^1.18.2 || ^2.0.0", prettier@^2.6.2: +"prettier@^1.18.2 || ^2.0.0", prettier@^2.3.1, prettier@^2.6.2: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -15265,6 +22207,11 @@ proc-log@^3.0.0: resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== +proc-log@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== + process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -15280,7 +22227,7 @@ process-warning@^1.0.0: resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== -process@^0.11.10: +process@0.11.10, process@^0.11.1, process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== @@ -15302,6 +22249,26 @@ promise-poller@^1.9.1: dependencies: debug "^4.1.0" +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +promise-settle@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/promise-settle/-/promise-settle-0.3.0.tgz#b4efd572a1eb74cf794f828cd349da40a08e4e96" + integrity sha512-sdZv9X6V2mnVSNibIHk44hI2jf6z9bhsm2OUU+hsU1JgdFuwsZVUcIGPreQ9wlu5wWlAGXzbyEbCUU+U8TQSHQ== + +promise@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== + dependencies: + asap "~2.0.6" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -15310,11 +22277,34 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== +protobufjs@^7.2.0, protobufjs@^7.2.5: + version "7.3.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.3.0.tgz#a32ec0422c039798c41a0700306a6e305b9cb32c" + integrity sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + protobufjs@^7.2.4: version "7.2.6" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" @@ -15474,6 +22464,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.11.0, qs@^6.7.0: + version "6.12.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" + integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== + dependencies: + side-channel "^1.0.6" + qs@^6.11.2: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" @@ -15530,7 +22527,7 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== -queue-microtask@^1.2.2: +queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== @@ -15580,7 +22577,7 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@2.5.2: +raw-body@2.5.2, raw-body@^2.4.1, raw-body@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== @@ -15624,6 +22621,11 @@ react-native-webview@^11.26.0: escape-string-regexp "2.0.0" invariant "2.2.4" +react-refresh@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" + integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ== + react@>=17, react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -15631,6 +22633,13 @@ react@>=17, react@^18.2.0: dependencies: loose-envify "^1.1.0" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + read-pkg@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -15667,7 +22676,7 @@ readable-stream@2.3.3: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: +readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.1, readable-stream@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -15697,6 +22706,20 @@ real-require@^0.1.0: resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + redis-errors@^1.0.0, redis-errors@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" @@ -15709,6 +22732,16 @@ redis-parser@^3.0.0: dependencies: redis-errors "^1.0.0" +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + +reflect-metadata@^0.1.13: + version "0.1.14" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" + integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== + regenerate-unicode-properties@^10.1.0: version "10.1.1" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" @@ -15801,6 +22834,25 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== +req-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" + integrity sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ== + dependencies: + req-from "^2.0.0" + +req-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-from/-/req-from-2.0.0.tgz#d74188e47f93796f4aa71df6ee35ae689f3e0e70" + integrity sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA== + dependencies: + resolve-from "^3.0.0" + +request-ip@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-3.3.0.tgz#863451e8fec03847d44f223e30a5d63e369fa611" + integrity sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA== + request-progress@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" @@ -15839,6 +22891,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.0, require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -15898,7 +22955,19 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.3.2: +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== + +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.3.2: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -15935,6 +23004,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@0.13.1, retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -15960,7 +23034,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg== -rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -15974,6 +23048,11 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +ringbufferjs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ringbufferjs/-/ringbufferjs-2.0.0.tgz#09f40e2675a99cfef430b7ec5815ac1bc2e24120" + integrity sha512-GCOqTzUsTHF7nrqcgtNGAFotXztLgiePpIDpyWZ7R5I02tmfJWV+/yuJc//Hlsd8G+WzI1t/dc2y/w2imDZdog== + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -15982,7 +23061,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.2.4: +rlp@^2.2.3, rlp@^2.2.4: version "2.2.7" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== @@ -16033,6 +23112,13 @@ rrweb-cssom@^0.6.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== +run-parallel-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" + integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== + dependencies: + queue-microtask "^1.2.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -16047,6 +23133,11 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + rxjs@7.5.1: version "7.5.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.1.tgz#af73df343cbcab37628197f43ea0c8256f54b157" @@ -16054,7 +23145,7 @@ rxjs@7.5.1: dependencies: tslib "^2.1.0" -rxjs@^7.5.1: +rxjs@^7.5.1, rxjs@^7.8.0: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -16097,7 +23188,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -safe-stable-stringify@^2.1.0: +safe-stable-stringify@^2.1.0, safe-stable-stringify@^2.3.1: version "2.4.3" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== @@ -16107,6 +23198,14 @@ safe-stable-stringify@^2.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass-loader@^12.2.0: + version "12.6.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.6.0.tgz#5148362c8e2cdd4b950f3c63ac5d16dbfed37bcb" + integrity sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA== + dependencies: + klona "^2.0.4" + neo-async "^2.6.2" + sass-loader@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" @@ -16127,6 +23226,20 @@ sass@^1.26.5: immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" +sass@^1.42.1: + version "1.77.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.1.tgz#018cdfb206afd14724030c02e9fefd8f30a76cd0" + integrity sha512-OMEyfirt9XEfyvocduUIOlUSkWOXS/LAt6oblR/ISXCTukyavjex+zQNm51pPCOiFKY1QpWvEH1EeCkgyV3I6w== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -16139,6 +23252,26 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +sc-istanbul@^0.4.5: + version "0.4.6" + resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" + integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -16173,6 +23306,25 @@ schema-utils@^2.0.0, schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6 ajv "^6.12.4" ajv-keywords "^3.5.2" +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0, schema-utils@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" @@ -16201,6 +23353,11 @@ secure-compare@3.0.1: resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw== +secure-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/secure-keys/-/secure-keys-1.0.0.tgz#f0c82d98a3b139a8776a8808050b824431087fca" + integrity sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -16218,6 +23375,14 @@ selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" +selfsigned@^2.1.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -16235,6 +23400,11 @@ semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semve dependencies: lru-cache "^6.0.0" +semver@^7.3.4, semver@^7.5.2, semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -16261,6 +23431,13 @@ serialize-error@^8.1.0: dependencies: type-fest "^0.20.2" +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -16268,6 +23445,13 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -16362,6 +23546,14 @@ sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +sha1@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" + integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA== + dependencies: + charenc ">= 0.0.1" + crypt ">= 0.0.1" + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -16398,6 +23590,23 @@ shell-quote@^1.8.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== +shelljs@^0.8.3: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +short-uuid@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/short-uuid/-/short-uuid-5.2.0.tgz#49378f8c5335a603bc801c279ae521c5d22532dc" + integrity sha512-296/Nzi4DmANh93iYBwT4NoYRJuHnKEzefrkSagQbTH/A6NTaB68hSPDjm5IlbI5dx9FXdmtqPcj6N5H+CPm6w== + dependencies: + any-base "^1.1.0" + uuid "^9.0.1" + side-channel@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" @@ -16418,6 +23627,11 @@ side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +sift@17.1.3: + version "17.1.3" + resolved "https://registry.yarnpkg.com/sift/-/sift-17.1.3.tgz#9d2000d4d41586880b0079b5183d839c7a142bf7" + integrity sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ== + siginfo@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" @@ -16447,6 +23661,15 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +simple-get@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55" + integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -16468,6 +23691,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +sjcl@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/sjcl/-/sjcl-1.0.8.tgz#f2ec8d7dc1f0f21b069b8914a41a8f236b0e252a" + integrity sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ== + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -16483,6 +23711,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" @@ -16501,6 +23734,19 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -16560,7 +23806,7 @@ sockjs-client@^1.5.0: inherits "^2.0.4" url-parse "^1.5.10" -sockjs@^0.3.21: +sockjs@^0.3.21, sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== @@ -16569,6 +23815,63 @@ sockjs@^0.3.21: uuid "^8.3.2" websocket-driver "^0.7.4" +socks-proxy-agent@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d" + integrity sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.7.1" + +socks@^2.7.1: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solidity-coverage@^0.8.0: + version "0.8.12" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.12.tgz#c4fa2f64eff8ada7a1387b235d6b5b0e6c6985ed" + integrity sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw== + dependencies: + "@ethersproject/abi" "^5.0.9" + "@solidity-parser/parser" "^0.18.0" + chalk "^2.4.2" + death "^1.1.0" + difflib "^0.2.4" + fs-extra "^8.1.0" + ghost-testrpc "^0.0.2" + global-modules "^2.0.0" + globby "^10.0.1" + jsonschema "^1.2.4" + lodash "^4.17.21" + mocha "^10.2.0" + node-emoji "^1.10.0" + pify "^4.0.1" + recursive-readdir "^2.2.2" + sc-istanbul "^0.4.5" + semver "^7.3.4" + shelljs "^0.8.3" + web3-utils "^1.3.6" + sonic-boom@^2.2.1: version "2.8.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" @@ -16593,6 +23896,20 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-js@^1.0.1, source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-loader@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-3.0.2.tgz#af23192f9b344daa729f6772933194cc5fa54fee" + integrity sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg== + dependencies: + abab "^2.0.5" + iconv-lite "^0.6.3" + source-map-js "^1.0.1" + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -16620,7 +23937,7 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.21, source-map-support@~0.5.12: +source-map-support@^0.5.13, source-map-support@^0.5.21, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -16653,6 +23970,20 @@ source-map@^0.7.3, source-map@^0.7.4: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA== + dependencies: + amdefine ">=0.0.4" + +sparse-bitfield@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" + integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ== + dependencies: + memory-pager "^1.0.2" + spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -16719,6 +24050,11 @@ split2@^4.0.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -16739,6 +24075,13 @@ sshpk@^1.14.1, sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +ssri@^10.0.0: + version "10.0.6" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" + integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== + dependencies: + minipass "^7.0.3" + ssri@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" @@ -16758,6 +24101,11 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -16775,6 +24123,13 @@ stackframe@^1.3.4: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + standard-as-callback@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" @@ -16793,7 +24148,7 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.4.0 < 2": +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -16803,6 +24158,11 @@ std-env@^3.5.0, std-env@^3.7.0: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -16853,6 +24213,20 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -16863,6 +24237,11 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== +string-format@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" + integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -16871,7 +24250,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16880,6 +24259,14 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -16898,6 +24285,11 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.fromcodepoint@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz#8d978333c0bc92538f50f383e4888f3e5619d653" + integrity sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg== + string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" @@ -16960,6 +24352,13 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -17011,16 +24410,16 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA== +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - strip-literal@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-2.0.0.tgz#5d063580933e4e03ebb669b12db64d2200687527" @@ -17028,6 +24427,11 @@ strip-literal@^2.0.0: dependencies: js-tokens "^8.0.2" +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -17037,6 +24441,16 @@ strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" +sturdy-websocket@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/sturdy-websocket/-/sturdy-websocket-0.2.1.tgz#20a58fd53372ef96eaa08f3c61c91a10b07c7c05" + integrity sha512-NnzSOEKyv4I83qbuKw9ROtJrrT6Z/Xt7I0HiP/e6H6GnpeTDvzwGIGeJ8slai+VwODSHQDooW2CAilJwT9SpRg== + +style-loader@^3.3.0: + version "3.3.4" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" + integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -17046,6 +24460,14 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +stylehacks@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.1.1.tgz#543f91c10d17d00a440430362d419f79c25545a6" + integrity sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg== + dependencies: + browserslist "^4.23.0" + postcss-selector-parser "^6.0.16" + stylis@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" @@ -17060,6 +24482,14 @@ stylus-loader@^3.0.2: lodash.clonedeep "^4.5.0" when "~3.6.x" +stylus-loader@^7.1.0: + version "7.1.3" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-7.1.3.tgz#1fdfa0d34e8c05a569bc0902e1ecdb857d764964" + integrity sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw== + dependencies: + fast-glob "^3.2.12" + normalize-path "^3.0.0" + stylus@^0.54.7: version "0.54.8" resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" @@ -17074,16 +24504,64 @@ stylus@^0.54.7: semver "^6.3.0" source-map "^0.7.3" +stylus@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.59.0.tgz#a344d5932787142a141946536d6e24e6a6be7aa6" + integrity sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg== + dependencies: + "@adobe/css-tools" "^4.0.1" + debug "^4.3.2" + glob "^7.1.6" + sax "~1.2.4" + source-map "^0.7.3" + +superagent@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-9.0.2.tgz#a18799473fc57557289d6b63960610e358bdebc1" + integrity sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.4" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^3.5.1" + methods "^1.1.2" + mime "2.6.0" + qs "^6.11.0" + superstruct@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.3.tgz#de626a5b49c6641ff4d37da3c7598e7a87697046" integrity sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg== +supertest@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.0.0.tgz#cac53b3d6872a0b317980b2b0cfa820f09cd7634" + integrity sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA== + dependencies: + methods "^1.1.2" + superagent "^9.0.1" + +supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== +supports-color@^3.1.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A== + dependencies: + has-flag "^1.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -17105,18 +24583,16 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + svg-tags@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" @@ -17141,6 +24617,19 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" +svgo@^3.0.2, svgo@^3.2.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" + integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.0.0" + swarm-js@^0.1.40: version "0.1.42" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" @@ -17170,22 +24659,69 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +sync-request@^6.0.0, sync-request@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" + integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== + dependencies: + http-response-object "^3.0.1" + sync-rpc "^1.2.1" + then-request "^6.0.0" + +sync-rpc@^1.2.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" + integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== + dependencies: + get-port "^3.1.0" + system-architecture@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d" integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== +table-layout@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + +table@^6.8.0: + version "6.8.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.2.0: +tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar-stream@~2.2.0: +tar-fs@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4, tar-stream@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== @@ -17209,6 +24745,18 @@ tar@^4.0.2: safe-buffer "^5.2.1" yallist "^3.1.1" +tar@^6.1.11, tar@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^1.4.4: version "1.4.5" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" @@ -17224,6 +24772,17 @@ terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^1.4.4: webpack-sources "^1.4.0" worker-farm "^1.7.0" +terser-webpack-plugin@^5.3.10, terser-webpack-plugin@^5.3.3: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + terser@^4.1.2: version "4.8.1" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" @@ -17233,6 +24792,16 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.26.0: + version "5.31.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.0.tgz#06eef86f17007dbad4593f11a574c7f5eb02c6a1" + integrity sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -17242,11 +24811,33 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +then-request@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" + integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== + dependencies: + "@types/concat-stream" "^1.6.0" + "@types/form-data" "0.0.33" + "@types/node" "^8.0.0" + "@types/qs" "^6.2.31" + caseless "~0.12.0" + concat-stream "^1.6.0" + form-data "^2.2.0" + http-basic "^8.1.1" + http-response-object "^3.0.1" + promise "^8.0.0" + qs "^6.4.0" + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -17327,16 +24918,23 @@ tinybench@^2.5.1: resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.6.0.tgz#1423284ee22de07c91b3752c048d2764714b341b" integrity sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA== -tinypool@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.2.tgz#84013b03dc69dacb322563a475d4c0a9be00f82a" - integrity sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ== +tinypool@^0.8.3: + version "0.8.4" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8" + integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ== tinyspy@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1" integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -17443,21 +25041,41 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== -ts-api-utils@^1.0.1: +ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== +ts-command-line-args@^2.2.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" + integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== + dependencies: + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-usage "^6.1.0" + string-format "^2.0.0" + ts-custom-error@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/ts-custom-error/-/ts-custom-error-3.3.1.tgz#8bd3c8fc6b8dc8e1cb329267c45200f1e17a65d1" integrity sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A== +ts-essentials@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" + integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== + ts-jest@29.1.2: version "29.1.2" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" @@ -17483,6 +25101,22 @@ ts-loader@^6.2.2: micromatch "^4.0.0" semver "^6.0.0" +ts-loader@^9.3.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" + integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" + +ts-mixer@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.4.tgz#1da39ceabc09d947a82140d9f09db0f84919ca28" + integrity sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA== + ts-node@10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -17521,6 +25155,15 @@ tsconfig-paths-webpack-plugin@3.5.2: enhanced-resolve "^5.7.0" tsconfig-paths "^3.9.0" +tsconfig-paths-webpack-plugin@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.0.tgz#84008fc3e3e0658fdb0262758b07b4da6265ff1a" + integrity sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.7.0" + tsconfig-paths "^4.0.0" + tsconfig-paths@^3.15.0, tsconfig-paths@^3.9.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -17531,7 +25174,7 @@ tsconfig-paths@^3.15.0, tsconfig-paths@^3.9.0: minimist "^1.2.6" strip-bom "^3.0.0" -tsconfig-paths@^4.1.2: +tsconfig-paths@^4.0.0, tsconfig-paths@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== @@ -17550,12 +25193,12 @@ tsconfig@^7.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@1.14.1, tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1: +tslib@1.14.1, tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.2: +tslib@2.6.2, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -17579,6 +25222,16 @@ tslint@^5.20.1: tslib "^1.8.0" tsutils "^2.29.0" +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== + +tsscmp@1.0.6, tsscmp@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tsutils@^2.29.0: version "2.29.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" @@ -17603,11 +25256,21 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -17615,6 +25278,13 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -17635,7 +25305,12 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-is@~1.6.18: +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +type-is@^1.6.16, type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -17653,6 +25328,22 @@ type@^2.7.2: resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== +typechain@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.2.tgz#1090dd8d9c57b6ef2aed3640a516bdbf01b00d73" + integrity sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q== + dependencies: + "@types/prettier" "^2.1.1" + debug "^4.3.1" + fs-extra "^7.0.0" + glob "7.1.7" + js-sha3 "^0.8.0" + lodash "^4.17.15" + mkdirp "^1.0.4" + prettier "^2.3.1" + ts-command-line-args "^2.2.0" + ts-essentials "^7.0.1" + typed-array-buffer@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" @@ -17697,6 +25388,11 @@ typed-array-length@^1.0.4: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typed-assert@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/typed-assert/-/typed-assert-1.0.9.tgz#8af9d4f93432c4970ec717e3006f33f135b06213" + integrity sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -17714,10 +25410,20 @@ typescript@4.5.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== -typescript@^5.3.3, typescript@~5.3.2: - version "5.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" - integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== +typescript@5.4.5, typescript@~5.4.2: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== ufo@^1.3.0, ufo@^1.3.1, ufo@^1.3.2: version "1.4.0" @@ -17732,6 +25438,11 @@ uglify-js@3.4.x: commander "~2.19.0" source-map "~0.6.1" +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + uint8arrays@^3.0.0, uint8arrays@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" @@ -17764,6 +25475,18 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-6.13.0.tgz#7edbf4b7f3aac5f8a681d515151bf55cb3589d72" + integrity sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw== + +undici@^5.14.0: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + unenv@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.9.0.tgz#469502ae85be1bd3a6aa60f810972b1a904ca312" @@ -17775,6 +25498,20 @@ unenv@^1.9.0: node-fetch-native "^1.6.1" pathe "^1.1.1" +unescape-js@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unescape-js/-/unescape-js-1.1.4.tgz#4bc6389c499cb055a98364a0b3094e1c3d5da395" + integrity sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g== + dependencies: + string.fromcodepoint "^0.2.1" + +unescape@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unescape/-/unescape-1.0.1.tgz#956e430f61cad8a4d57d82c518f5e6cc5d0dda96" + integrity sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ== + dependencies: + extend-shallow "^2.0.1" + unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" @@ -17837,6 +25574,13 @@ unique-filename@^1.1.1: dependencies: unique-slug "^2.0.0" +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -17844,6 +25588,18 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + +unique-username-generator@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/unique-username-generator/-/unique-username-generator-1.3.0.tgz#f1da1977fcd4d9f2297795960a0cff6bbda87601" + integrity sha512-oPs+Jq9UlXmcrpzrpYvWOWVUVLkJ0/x8zM66ikWEBe1l/hBqXS5fofsDvdScLIlPtqQKlu8/Y91b6T2XtIFubQ== + universal-user-agent@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" @@ -17977,7 +25733,7 @@ uqr@^0.1.2: resolved "https://registry.yarnpkg.com/uqr/-/uqr-0.1.2.tgz#5c6cd5dcff9581f9bb35b982cb89e2c483a41d7d" integrity sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA== -uri-js@^4.2.2: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -18003,7 +25759,7 @@ url-loader@^2.2.0: mime "^2.4.4" schema-utils "^2.5.0" -url-parse@^1.5.10, url-parse@^1.5.3: +url-parse@^1.4.3, url-parse@^1.5.10, url-parse@^1.5.3: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== @@ -18016,6 +25772,11 @@ url-set-query@^1.0.0: resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== +url-template@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + integrity sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw== + url@^0.11.0: version "0.11.3" resolved "https://registry.yarnpkg.com/url/-/url-0.11.3.tgz#6f495f4b935de40ce4a0a52faee8954244f3d3ad" @@ -18076,7 +25837,7 @@ util.promisify@~1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -util@^0.10.4: +util@^0.10.3, util@^0.10.4: version "0.10.4" resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== @@ -18121,7 +25882,7 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -18131,7 +25892,7 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1, v8-to-istanbul@^9.2.0: +v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: version "9.2.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== @@ -18155,6 +25916,11 @@ validate-npm-package-name@^5.0.0: dependencies: builtins "^5.0.0" +validator@^13.9.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + valtio@1.11.2: version "1.11.2" resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.11.2.tgz#b8049c02dfe65620635d23ebae9121a741bb6530" @@ -18168,7 +25934,7 @@ varint@^5.0.0: resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== -vary@^1, vary@~1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -18229,10 +25995,10 @@ viem@^2.7.13: isows "1.0.3" ws "8.13.0" -vite-node@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.4.0.tgz#265529d60570ca695ceb69391f87f92847934ad8" - integrity sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw== +vite-node@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.6.0.tgz#2c7e61129bfecc759478fa592754fd9704aaba7f" + integrity sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw== dependencies: cac "^6.7.14" debug "^4.3.4" @@ -18287,16 +26053,16 @@ vite@5.1.6, vite@^5.0.0: optionalDependencies: fsevents "~2.3.3" -vitest@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.4.0.tgz#f5c812aaf5023818b89b7fc667fa45327396fece" - integrity sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw== - dependencies: - "@vitest/expect" "1.4.0" - "@vitest/runner" "1.4.0" - "@vitest/snapshot" "1.4.0" - "@vitest/spy" "1.4.0" - "@vitest/utils" "1.4.0" +vitest@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.6.0.tgz#9d5ad4752a3c451be919e412c597126cffb9892f" + integrity sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA== + dependencies: + "@vitest/expect" "1.6.0" + "@vitest/runner" "1.6.0" + "@vitest/snapshot" "1.6.0" + "@vitest/spy" "1.6.0" + "@vitest/utils" "1.6.0" acorn-walk "^8.3.2" chai "^4.3.10" debug "^4.3.4" @@ -18308,9 +26074,9 @@ vitest@^1.3.1: std-env "^3.5.0" strip-literal "^2.0.0" tinybench "^2.5.1" - tinypool "^0.8.2" + tinypool "^0.8.3" vite "^5.0.0" - vite-node "1.4.0" + vite-node "1.6.0" why-is-node-running "^2.2.2" vm-browserify@^1.0.1: @@ -18473,6 +26239,14 @@ watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1" +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" @@ -18690,7 +26464,7 @@ web3-shh@1.10.4: web3-core-subscriptions "1.10.4" web3-net "1.10.4" -web3-utils@1.10.4, web3-utils@^1.8.1, web3-utils@^1.8.2: +web3-utils@1.10.4, web3-utils@^1.3.6, web3-utils@^1.8.1, web3-utils@^1.8.2: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== @@ -18782,6 +26556,17 @@ webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" +webpack-dev-middleware@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" + integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + webpack-dev-server@^3.11.0: version "3.11.3" resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz#8c86b9d2812bf135d3c9bce6f07b718e30f7c3d3" @@ -18821,6 +26606,42 @@ webpack-dev-server@^3.11.0: ws "^6.2.1" yargs "^13.3.2" +webpack-dev-server@^4.9.3: + version "4.15.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz#9e0c70a42a012560860adb186986da1248333173" + integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.5" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.4" + ws "^8.13.0" + webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" @@ -18836,6 +26657,11 @@ webpack-merge@^4.2.2: dependencies: lodash "^4.17.15" +webpack-node-externals@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917" + integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ== + webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" @@ -18844,11 +26670,18 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^3.2.3: +webpack-sources@^3.0.0, webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +webpack-subresource-integrity@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz#8b7606b033c6ccac14e684267cb7fb1f5c2a132a" + integrity sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q== + dependencies: + typed-assert "^1.0.8" + webpack-virtual-modules@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c" @@ -18888,6 +26721,36 @@ webpack@^4.0.0: watchpack "^1.7.4" webpack-sources "^1.4.1" +webpack@^5.80.0: + version "5.91.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" + integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.16.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" @@ -18914,6 +26777,18 @@ websocket@^1.0.32: utf-8-validate "^5.0.2" yaeti "^0.0.6" +websocket@^1.0.34: + version "1.0.35" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.35.tgz#374197207d7d4cc4c36cbf8a1bb886ee52a07885" + integrity sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.63" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + whatwg-encoding@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" @@ -18947,6 +26822,14 @@ whatwg-url@^12.0.0, whatwg-url@^12.0.1: tr46 "^4.1.1" webidl-conversions "^7.0.0" +whatwg-url@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-13.0.0.tgz#b7b536aca48306394a34e44bda8e99f332410f8f" + integrity sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -18987,7 +26870,7 @@ which-typed-array@^1.1.14, which-typed-array@^1.1.2: gopd "^1.0.1" has-tostringtag "^1.0.1" -which@^1.2.9: +which@^1.1.1, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -19001,6 +26884,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + why-is-node-running@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" @@ -19009,6 +26899,69 @@ why-is-node-running@^2.2.2: siginfo "^2.0.0" stackback "0.0.2" +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +winston-transport@^4.5.0, winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^2.4.5: + version "2.4.7" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.7.tgz#5791fe08ea7e90db090f1cb31ef98f32531062f1" + integrity sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg== + dependencies: + async "^2.6.4" + colors "1.0.x" + cycle "1.0.x" + eyes "0.1.x" + isstream "0.1.x" + stack-trace "0.0.x" + +winston@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.0.tgz#e76c0d722f78e04838158c61adc1287201de7ce3" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + +word-wrap@~1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -19023,6 +26976,11 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -19098,7 +27056,7 @@ ws@^6.0.0, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.5.1: +ws@^7.4.6, ws@^7.5.1: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== @@ -19108,6 +27066,11 @@ ws@^8.11.0, ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== +ws@^8.14.2, ws@^8.16.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" + integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== + ws@~8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" @@ -19198,6 +27161,16 @@ yaml@^1.10.0, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" + integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" @@ -19224,6 +27197,29 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.9: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0, yargs@^16.0.0, yargs@^16.1.1, yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -19257,20 +27253,7 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.0, yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: +yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -19296,11 +27279,21 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +ylru@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.4.0.tgz#0cf0aa57e9c24f8a2cbde0cc1ca2c9592ac4e0f6" + integrity sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA== + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +yn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-4.0.0.tgz#611480051ea43b510da1dfdbe177ed159f00a979" + integrity sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" @@ -19321,11 +27314,6 @@ yorkie@^2.0.0: normalize-path "^1.0.0" strip-indent "^2.0.0" -zksync-web3@^0.14.3: - version "0.14.4" - resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.14.4.tgz#0b70a7e1a9d45cc57c0971736079185746d46b1f" - integrity sha512-kYehMD/S6Uhe1g434UnaMN+sBr9nQm23Ywn0EUP5BfQCsbjcr3ORuS68PosZw8xUTu3pac7G6YMSnNHk+fwzvg== - zod@3.22.4: version "3.22.4" resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"