From 85d51b4799525987ebbbddbd05fa1bd3cab7ba71 Mon Sep 17 00:00:00 2001 From: Rohit Durvasula Date: Wed, 10 Jan 2024 15:06:21 -0800 Subject: [PATCH] Release v0.4.2 --- .eslintrc.json | 33 + .github/workflows/release.yaml | 27 + .gitignore | 40 + LICENSE | 13 + README.md | 82 + docs/openapi/orchestration.swagger.json | 1751 ++++++++++++ package-lock.json | 2394 +++++++++++++++++ package.json | 39 + src/auth/index.ts | 173 ++ src/client/protocols/ethereum_kiln_staking.ts | 115 + src/client/staking-service-client.ts | 262 ++ src/examples/public/example.ts | 15 + src/examples/public/example_e2e_staking.ts | 166 ++ .../coinbase/staking/v1alpha1/action.pb.ts | 16 + src/gen/coinbase/staking/v1alpha1/api.pb.ts | 45 + .../coinbase/staking/v1alpha1/common.pb.ts | 9 + .../staking/v1alpha1/ethereum_kiln.pb.ts | 52 + .../coinbase/staking/v1alpha1/network.pb.ts | 17 + .../coinbase/staking/v1alpha1/polygon.pb.ts | 44 + .../coinbase/staking/v1alpha1/protocol.pb.ts | 15 + .../coinbase/staking/v1alpha1/solana.pb.ts | 73 + .../staking/v1alpha1/staking_context.pb.ts | 32 + .../staking/v1alpha1/staking_target.pb.ts | 42 + .../coinbase/staking/v1alpha1/workflow.pb.ts | 139 + src/gen/fetch.pb.ts | 341 +++ src/gen/google/protobuf/timestamp.pb.ts | 9 + src/index.ts | 5 + src/signers/ethereum_signer.ts | 26 + src/signers/index.ts | 2 + src/signers/txsigner.ts | 18 + src/utils/date.ts | 10 + tsconfig.json | 16 + 32 files changed, 6021 insertions(+) create mode 100644 .eslintrc.json create mode 100644 .github/workflows/release.yaml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/openapi/orchestration.swagger.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/auth/index.ts create mode 100644 src/client/protocols/ethereum_kiln_staking.ts create mode 100644 src/client/staking-service-client.ts create mode 100644 src/examples/public/example.ts create mode 100644 src/examples/public/example_e2e_staking.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/action.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/api.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/common.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/network.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/polygon.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/protocol.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/solana.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/staking_target.pb.ts create mode 100644 src/gen/coinbase/staking/v1alpha1/workflow.pb.ts create mode 100644 src/gen/fetch.pb.ts create mode 100644 src/gen/google/protobuf/timestamp.pb.ts create mode 100644 src/index.ts create mode 100644 src/signers/ethereum_signer.ts create mode 100644 src/signers/index.ts create mode 100644 src/signers/txsigner.ts create mode 100644 src/utils/date.ts create mode 100644 tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..dc356b7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "env": { + "es6": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "prettier" + ], + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint", + "prettier" + ], + "rules": { + "no-unused-vars": "warn", + "no-constant-condition": "warn", + "no-empty": "warn", + "consistent-return": "error", + "@typescript-eslint/no-empty-function": "warn", + "prettier/prettier": "warn", + "prefer-const": "off" + } +} \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..d201051 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,27 @@ +name: Version 🔖 + +on: + release: + types: [created] + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + version: + name: Release + runs-on: ubuntu-latest + environment: release + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18.x' + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm@^9.5.0 + - run: npm ci + - run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f96b19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# IDE/Tools +.docker +.idea +/.vscode +# vim +.*.sw? + +# Local +.DS_Store + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ + +# Cloud API key +.coinbase_cloud_api_key*.json + +# Example binary +staking-client-* + +# environment variables +.env +.envrc + +# Output directory +dist/ + +node_modules/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..76ec30d --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2018-2023 Coinbase, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..96e31ff --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Staking API Typescript Client Library + +[![npm version](https://badge.fury.io/js/@coinbase%2Fstaking-client-library-ts.svg)](https://badge.fury.io/js/@coinbase%2Fstaking-client-library-ts) + +This repository contains the Protocol Buffer definitions for the Coinbase **Staking API**, as well as the Typescript client libraries generated from them. + +## Overview + +Staking API provides a set of APIs to aid in non-custodial staking for multiple protocols and networks. + +## Prerequisites + +- [Node 18.12.0 or higher](https://nodejs.org/en/blog/release/v18.12.0) + +## Repository Structure +- [`src/auth/`](./src/auth/) contains the authentication-related code for accessing Coinbase Cloud APIs. +- [`src/client/`](./src/client/) contains client instantiation helpers for Staking APIs. +- [`src/gen/`](./src/gen/) contains Typescript code generated from the Protocol Buffers. +- [`src/examples/`](./src/examples/) contains examples to consume the client library. + +## Get Started +To test that your API Key gives you access as expected to the Staking APIs: + +1. Clone this GitHub repo +2. Download your API key from the Coinbase Cloud UI and save it as `.coinbase_cloud_api_key.json` at the root of this repo +3. Run `npm install && npm run build` +4. Run `ts-node src/examples/public/example.ts` +5. You should see output like the following: + ```text + { + actions: [ + { + name: 'protocols/ethereum_kiln/networks/goerli/actions/stake' + }, + { + name: 'protocols/ethereum_kiln/networks/goerli/actions/unstake' + }, + { + name: 'protocols/ethereum_kiln/networks/goerli/actions/claim_stake' + } + ] + } + { + protocols: [ + { + name: 'protocols/ethereum_kiln' + } + ] + } + { + networks: [ + { + name: 'protocols/ethereum_kiln/networks/goerli', + isMainnet: false + }, + { + name: 'protocols/ethereum_kiln/networks/mainnet', + isMainnet: true + } + ] + } + ``` + +## Running example from your application + +1. Install this package in your application - `npm install @coinbase/staking-client-library-ts` +2. Add your API key to the root of your application as `.coinbase_cloud_api_key.json` +3. Run example code: + + ```typescript + import { StakingServiceClient } from "@coinbase/staking-client-library-ts"; + + const client = new StakingServiceClient(); + + const exampleFunction = () => { + client.listProtocols().then((response) => { + console.log(response); + }); + }; + + exampleFunction(); + ``` diff --git a/docs/openapi/orchestration.swagger.json b/docs/openapi/orchestration.swagger.json new file mode 100644 index 0000000..1ad5810 --- /dev/null +++ b/docs/openapi/orchestration.swagger.json @@ -0,0 +1,1751 @@ +{ + "swagger": "2.0", + "info": { + "title": "Coinbase Non-Custodial Staking API", + "description": "Staking Service is a non-custodial staking service that aims to support multiple protocols and actions on them.", + "version": "v1alpha1" + }, + "tags": [ + { + "name": "Protocol", + "description": "Protocols details" + }, + { + "name": "Network", + "description": "Networks details" + }, + { + "name": "Validator", + "description": "Validators details" + }, + { + "name": "Action", + "description": "Actions details" + }, + { + "name": "Workflow", + "description": "Workflow management details" + }, + { + "name": "StakingService" + } + ], + "host": "api.developer.coinbase.com", + "basePath": "/staking", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/api/v1alpha1/protocols": { + "get": { + "summary": "List supported protocols", + "description": "List supported protocols", + "operationId": "listProtocols", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1ListProtocolsResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "Protocol" + ] + } + }, + "/api/v1alpha1/viewStakingContext:view": { + "get": { + "summary": "Returns point-in-time context of staking data for an address", + "description": "Returns point-in-time context of staking data for an address", + "operationId": "ViewStakingContext", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1ViewStakingContextResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "address", + "description": "The address to fetch staking context for.", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "network", + "description": "The network to fetch staking context for.", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "ethereumKilnStakingContextParameters.integratorContractAddress", + "description": "Integrator contract address.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "StakingContext" + ] + } + }, + "/api/v1alpha1/{name}": { + "get": { + "summary": "Get workflow", + "operationId": "getWorkflow", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1Workflow" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "description": "The resource name of the workflow.\nFormat: projects/{project}/workflows/{workflow}", + "in": "path", + "required": true, + "type": "string", + "pattern": "projects/[^/]+/workflows/[^/]+" + } + ], + "tags": [ + "Workflow" + ] + } + }, + "/api/v1alpha1/{name}/refresh": { + "post": { + "summary": "Refresh the current step in a workflow", + "description": "Refresh the current step in a workflow", + "operationId": "refreshWorkflow", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1Workflow" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "description": "The resource name of the workflow.\nFormat: projects/{project}/workflows/{workflow}", + "in": "path", + "required": true, + "type": "string", + "pattern": "projects/[^/]+/workflows/[^/]+" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "step": { + "type": "integer", + "format": "int32", + "description": "The index of the step to be refreshed." + } + }, + "description": "The request message for RefreshWorkflowStepRequest.", + "required": [ + "step" + ] + } + } + ], + "tags": [ + "Workflow" + ] + } + }, + "/api/v1alpha1/{name}/step": { + "post": { + "summary": "Perform the next step in a workflow", + "description": "Perform the next step in a workflow", + "operationId": "updateWorkflow", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1Workflow" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "description": "The resource name of the workflow.\nFormat: projects/{project}/workflows/{workflow}", + "in": "path", + "required": true, + "type": "string", + "pattern": "projects/[^/]+/workflows/[^/]+" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "step": { + "type": "integer", + "format": "int32", + "description": "The index of the step to be performed." + }, + "data": { + "type": "string", + "description": "Transaction metadata. This is either the signed transaction or transaction hash depending on the workflow's broadcast method." + } + }, + "description": "The request message for PerformWorkflowStep.", + "required": [ + "step", + "data" + ] + } + } + ], + "tags": [ + "Workflow" + ] + } + }, + "/api/v1alpha1/{parent}/actions": { + "get": { + "summary": "List supported actions", + "description": "List supported actions", + "operationId": "listActions", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1ListActionsResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The resource name of the parent that owns\nthe collection of actions.\nFormat: protocols/{protocol}/networks/{network}", + "in": "path", + "required": true, + "type": "string", + "pattern": "protocols/[^/]+/networks/[^/]+" + } + ], + "tags": [ + "Action" + ] + } + }, + "/api/v1alpha1/{parent}/networks": { + "get": { + "summary": "List supported networks", + "description": "List supported networks", + "operationId": "listNetworks", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1ListNetworksResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The resource name of the parent that owns\nthe collection of networks.\nFormat: protocols/{protocol}", + "in": "path", + "required": true, + "type": "string", + "pattern": "protocols/[^/]+" + } + ], + "tags": [ + "Network" + ] + } + }, + "/api/v1alpha1/{parent}/stakingTargets": { + "get": { + "summary": "List supported staking targets", + "description": "List supported staking targets", + "operationId": "listStakingTargets", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1ListStakingTargetsResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The resource name of the parent that owns\nthe collection of staking targets.\nFormat: protocols/{protocol}/networks/{network}", + "in": "path", + "required": true, + "type": "string", + "pattern": "protocols/[^/]+/networks/[^/]+" + }, + { + "name": "pageSize", + "description": "The maximum number of staking targets to return. The service may\nreturn fewer than this value.\n\nIf unspecified, 100 staking targets will be returned.\nThe maximum value is 1000; values over 1000 will be floored to 1000.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "pageToken", + "description": "A page token as part of the response of a previous call.\nProvide this to retrieve the next page.\n\nWhen paginating, all other parameters must match the previous\nrequest to list resources.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "StakingTarget" + ] + } + }, + "/api/v1alpha1/{parent}/workflows": { + "get": { + "summary": "List supported workflows", + "operationId": "listWorkflows", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1ListWorkflowsResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The resource name of the parent that owns\nthe collection of networks.\nFormat: projects/{project}", + "in": "path", + "required": true, + "type": "string", + "pattern": "projects/[^/]+" + }, + { + "name": "filter", + "description": "[AIP-160](https://google.aip.dev/160) filter\nSupported fields:\n- string delegator_address: \"0x...\"\n- string validator_address: \"0x...\"\n- string action: \"stake\", \"unstake\"\n- string protocol: \"ethereum_kiln\"\n- string network: \"goerli\", \"mainnet\"\n- string amount: \"10000\"\n- string currency: \"ETH\"", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pageSize", + "description": "The maximum number of workflows to return. The service may\nreturn fewer than this value.\n\nIf unspecified, 100 workflows will be returned.\nThe maximum value is 1000; values over 1000 will be floored to 1000.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "pageToken", + "description": "A page token as part of the response of a previous call.\nProvide this to retrieve the next page.\n\nWhen paginating, all other parameters must match the previous\nrequest to list resources.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Workflow" + ] + }, + "post": { + "summary": "Create workflow", + "operationId": "createWorkflow", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha1Workflow" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "403": { + "description": "Returned when a user does not have permission to the resource.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "404": { + "description": "Returned when a resource is not found.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "429": { + "description": "Returned when a resource limit has been reached.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The resource name of the parent that owns\nthe workflow.\nFormat: projects/{project}", + "in": "path", + "required": true, + "type": "string", + "pattern": "projects/[^/]+" + }, + { + "name": "workflow", + "description": "The workflow to create.", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1alpha1Workflow", + "required": [ + "workflow" + ] + } + } + ], + "tags": [ + "Workflow" + ] + } + } + }, + "definitions": { + "WaitStepOutputWaitUnit": { + "type": "string", + "enum": [ + "WAIT_UNIT_UNSPECIFIED", + "WAIT_UNIT_SECONDS", + "WAIT_UNIT_BLOCKS", + "WAIT_UNIT_EPOCHS", + "WAIT_UNIT_CHECKPOINTS" + ], + "default": "WAIT_UNIT_UNSPECIFIED", + "description": "The unit of wait time.\n\n - WAIT_UNIT_UNSPECIFIED: Unspecified wait time.\n - WAIT_UNIT_SECONDS: Wait time measured in seconds.\n - WAIT_UNIT_BLOCKS: Wait time measured in blocks.\n - WAIT_UNIT_EPOCHS: Wait time measured in epochs.\n - WAIT_UNIT_CHECKPOINTS: Wait time measured in checkpoints." + }, + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/protobufAny" + } + } + } + }, + "v1alpha1Action": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "The resource name of the Action.\nFormat: protocols/{protocolName}/networks/{networkName}/actions/{actionName}\nEx: protocols/polygon/networks/goerli/validators/stake" + } + }, + "description": "An Action resource, which represents an action you may take on a network,\nposted to a validator (e.g. stake, unstake)." + }, + "v1alpha1Amount": { + "type": "object", + "properties": { + "value": { + "type": "string", + "description": "The total value of the token." + }, + "currency": { + "type": "string", + "description": "The currency of the token" + } + }, + "description": "The amount of a token you wish to perform an action\nwith." + }, + "v1alpha1Contract": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "The resource name of the Contract Address.\nFormat: protocols/{protocolName}/networks/{networkName}/stakingTargets/{contractName}\nEx: protocols/polygon/networks/goerli/stakingTargets/0x857679d69fE50E7B722f94aCd2629d80C355163d" + }, + "address": { + "type": "string", + "description": "The contract address you may submit actions to." + } + }, + "description": "A Contract resource, which represents an active contract\nfor the given protocol network which you can submit an action\nto." + }, + "v1alpha1EthereumKilnClaimStakeParameters": { + "type": "object", + "properties": { + "stakerAddress": { + "type": "string", + "description": "The address you wish to claim stake for." + }, + "integratorContractAddress": { + "type": "string", + "title": "The address of the integrator contract" + } + }, + "description": "The parameters required for the claim stake action on Ethereum Kiln.", + "title": "EthereumKiln: Claim Stake Parameters", + "required": [ + "stakerAddress", + "integratorContractAddress" + ] + }, + "v1alpha1EthereumKilnStakeParameters": { + "type": "object", + "properties": { + "stakerAddress": { + "type": "string", + "description": "The address you wish to stake from." + }, + "integratorContractAddress": { + "type": "string", + "description": "The address of the integrator contract." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount of Ethereum to stake in wei." + } + }, + "description": "The parameters required for the stake action on Ethereum Kiln.", + "title": "EthereumKiln: Stake Parameters", + "required": [ + "stakerAddress", + "integratorContractAddress", + "amount" + ] + }, + "v1alpha1EthereumKilnStakingContextDetails": { + "type": "object", + "properties": { + "ethereumBalance": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The Ethereum balance of the address.\nThis can be used to gate the stake action to make sure the requested stake amount\nis less than ethereum_balance." + }, + "integratorShareBalance": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The number of integrator shares owned by the address." + }, + "integratorShareUnderlyingBalance": { + "$ref": "#/definitions/v1alpha1Amount", + "title": "The total Ethereum you can exchange for your integrator shares.\nThis can be used to gate the unstake action to make sure the requested unstake amount\nis less than integrator_share_underlying_balance" + }, + "totalExitableEth": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The total amount of Ethereum you can redeem for all non-claimed vPool shares.\nThis along with the condition total_shares_pending_exit == fulfillable_share_count\ncan be used to gate the claim_stake action." + }, + "totalSharesPendingExit": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The number of vPool shares are eligible to receive now or at a later point in time." + }, + "fulfillableShareCount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The number of vPool shares you are able to claim now." + } + }, + "description": "The protocol specific details for an Ethereum Kiln staking context.", + "title": "EthereumKiln: Staking context details" + }, + "v1alpha1EthereumKilnStakingContextParameters": { + "type": "object", + "properties": { + "integratorContractAddress": { + "type": "string", + "description": "Integrator contract address." + } + }, + "description": "The protocol specific parameters required for fetching a staking context.", + "title": "EthereumKiln: Staking Context Parameters" + }, + "v1alpha1EthereumKilnStakingParameters": { + "type": "object", + "properties": { + "stakeParameters": { + "$ref": "#/definitions/v1alpha1EthereumKilnStakeParameters", + "description": "The parameters for stake action on Ethereum Kiln." + }, + "unstakeParameters": { + "$ref": "#/definitions/v1alpha1EthereumKilnUnstakeParameters", + "description": "The parameters for unstake action on Ethereum Kiln." + }, + "claimStakeParameters": { + "$ref": "#/definitions/v1alpha1EthereumKilnClaimStakeParameters", + "description": "The parameters for claim stake action on Ethereum Kiln." + } + }, + "description": "The parameters needed for staking on Ethereum via Kiln.", + "title": "EthereumKiln: Staking Parameters" + }, + "v1alpha1EthereumKilnUnstakeParameters": { + "type": "object", + "properties": { + "stakerAddress": { + "type": "string", + "description": "The address you wish to unstake from." + }, + "integratorContractAddress": { + "type": "string", + "description": "The address of the integrator contract." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount of Ethereum to unstake in wei." + } + }, + "description": "The parameters required for the unstake action on Ethereum Kiln.", + "title": "EthereumKiln: Unstake Parameters", + "required": [ + "stakerAddress", + "integratorContractAddress", + "amount" + ] + }, + "v1alpha1ListActionsResponse": { + "type": "object", + "properties": { + "actions": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1alpha1Action" + }, + "description": "The list of actions." + } + }, + "description": "The response message for ListActions." + }, + "v1alpha1ListNetworksResponse": { + "type": "object", + "properties": { + "networks": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1alpha1Network" + }, + "description": "The list of networks." + } + }, + "description": "The response message for ListNetworks." + }, + "v1alpha1ListProtocolsResponse": { + "type": "object", + "properties": { + "protocols": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1alpha1Protocol" + }, + "description": "The list of protocols." + } + }, + "description": "The response message for ListProtocols." + }, + "v1alpha1ListStakingTargetsResponse": { + "type": "object", + "properties": { + "stakingTargets": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1alpha1StakingTarget" + }, + "description": "The list of staking targets." + }, + "nextPageToken": { + "type": "string", + "description": "A token which can be provided as `page_token` to retrieve the next page.\nIf this field is omitted, there are no additional pages." + } + }, + "description": "The response message for ListStakingTargets." + }, + "v1alpha1ListWorkflowsResponse": { + "type": "object", + "properties": { + "workflows": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1alpha1Workflow" + }, + "description": "The list of workflows." + }, + "nextPageToken": { + "type": "string", + "description": "A token which can be provided as `page_token` to retrieve the next page.\nIf this field is omitted, there are no additional pages." + } + }, + "description": "The response message for ListWorkflows." + }, + "v1alpha1Network": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "The resource name of the Network.\nFormat: protocols/{protocolName}/networks/{networkName}\nEx: protocols/polygon/networks/goerli" + }, + "isMainnet": { + "type": "boolean", + "description": "Represents if the network is the mainnet network\nfor the given protocol." + } + }, + "title": "A Network resource, which represents a blockchain network.\n(e.g. mainnet, testnet, etc.)" + }, + "v1alpha1NonceOptions": { + "type": "object", + "properties": { + "nonce": { + "type": "string", + "description": "The blockhash stored in the nonce account." + }, + "nonceAccount": { + "type": "string", + "description": "The address of the nonce account." + }, + "nonceAuthority": { + "type": "string", + "description": "The address of the nonce authority. If not provided, defaults to the nonce_account_address. Signs the transaction." + } + }, + "description": "The parameters required to use a durable transaction nonce for Solana transactions.", + "title": "Solana: Nonce Options" + }, + "v1alpha1PolygonClaimRewardsParameters": { + "type": "object", + "properties": { + "delegatorAddress": { + "type": "string", + "description": "The public address of the delegator you wish to\ninteract with." + }, + "validatorAddress": { + "type": "string", + "description": "The public address of the validator you wish to\nperform the action to." + } + }, + "description": "The parameters required for claim rewards action on Polygon.", + "title": "The parameters needed to claim rewards on Polygon.", + "required": [ + "delegatorAddress", + "validatorAddress" + ] + }, + "v1alpha1PolygonRestakeParameters": { + "type": "object", + "properties": { + "delegatorAddress": { + "type": "string", + "description": "The public address of the delegator you wish to\ninteract with." + }, + "validatorAddress": { + "type": "string", + "description": "The public address of the validator you wish to\nperform the action to." + } + }, + "description": "The parameters required for unstake action on Polygon.", + "title": "The parameters needed to restake on Polygon.", + "required": [ + "delegatorAddress", + "validatorAddress" + ] + }, + "v1alpha1PolygonStakeParameters": { + "type": "object", + "properties": { + "delegatorAddress": { + "type": "string", + "description": "The public address of the delegator you wish to\ninteract with." + }, + "validatorAddress": { + "type": "string", + "description": "The public address of the validator you wish to\nperform the action to." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount of the asset. For native assets or ERC-20 contracts, this is presented in terms of\natomic units (e.g., Wei for Ether) as a base-10 number." + } + }, + "description": "The parameters required for stake action on Polygon.", + "title": "The parameters needed to stake on Polygon.", + "required": [ + "delegatorAddress", + "validatorAddress", + "amount" + ] + }, + "v1alpha1PolygonStakingParameters": { + "type": "object", + "properties": { + "stakeParameters": { + "$ref": "#/definitions/v1alpha1PolygonStakeParameters", + "description": "The parameters for stake action on Polygon." + }, + "unstakeParameters": { + "$ref": "#/definitions/v1alpha1PolygonUnstakeParameters", + "description": "The parameters for unstake action on Polygon." + }, + "restakeParameters": { + "$ref": "#/definitions/v1alpha1PolygonRestakeParameters", + "description": "The parameters for restake action on Polygon." + }, + "claimRewardsParameters": { + "$ref": "#/definitions/v1alpha1PolygonClaimRewardsParameters", + "description": "The parameters for claim rewards action on Polygon." + } + }, + "description": "The parameters needed for staking on Polygon." + }, + "v1alpha1PolygonUnstakeParameters": { + "type": "object", + "properties": { + "delegatorAddress": { + "type": "string", + "description": "The public address of the delegator you wish to\ninteract with." + }, + "validatorAddress": { + "type": "string", + "description": "The public address of the validator you wish to\nperform the action to." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount of the asset. For native assets or ERC-20 contracts, this is presented in terms of\natomic units (e.g., Wei for Ether) as a base-10 number." + } + }, + "description": "The parameters required for unstake action on Polygon.", + "title": "The parameters needed to unstake on Polygon.", + "required": [ + "delegatorAddress", + "validatorAddress", + "amount" + ] + }, + "v1alpha1Protocol": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "The resource name of the Protocol.\nFormat: protocols/{protocolName}\nEx: protocols/polygon" + } + }, + "description": "A Protocol resource (e.g. polygon, ethereum, etc.)." + }, + "v1alpha1SolanaCreateStakeAccountParameters": { + "type": "object", + "properties": { + "stakeAccountAddress": { + "type": "string", + "description": "The address of the new stake account which will be created. This address must not already exist. Signs the transaction." + }, + "fromAddress": { + "type": "string", + "description": "The address of the account which will fund the stake account. Pays the transaction fee. Signs the transaction." + }, + "stakeAuthority": { + "type": "string", + "description": "The address of the account which will be granted signing authority over staking operations on the new stake account. If not provided, defaults to the from_address." + }, + "withdrawAuthority": { + "type": "string", + "description": "The address of the account which will be granted signing authority over withdrawing inactive stake from the new stake account. If not provided, defaults to the from_address." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount to fund the stake account with, in Lamports." + }, + "nonceOptions": { + "$ref": "#/definitions/v1alpha1NonceOptions", + "description": "The parameters required to use a durable transaction nonce for Solana transactions." + } + }, + "description": "The parameters required for the create stake account action on Solana.", + "title": "Solana: Create Stake Account Parameters", + "required": [ + "stakeAccountAddress", + "fromAddress", + "amount" + ] + }, + "v1alpha1SolanaDeactivateStakeParameters": { + "type": "object", + "properties": { + "stakeAccountAddress": { + "type": "string", + "description": "The address of the stake account which will have its stake deactivated. Stake must be currently active." + }, + "stakeAuthority": { + "type": "string", + "description": "The address of the account which has signing authority over staking operations on the stake account. Signs the transaction." + }, + "nonceOptions": { + "$ref": "#/definitions/v1alpha1NonceOptions", + "description": "The parameters required to use a durable transaction nonce for Solana transactions." + } + }, + "description": "The parameters required for the deactivate stake action on Solana.", + "title": "Solana: Deactivate Stake Parameters", + "required": [ + "stakeAccountAddress", + "stakeAuthority" + ] + }, + "v1alpha1SolanaDelegateStakeParameters": { + "type": "object", + "properties": { + "stakeAccountAddress": { + "type": "string", + "description": "The address of the stake account which will be delegating its stake." + }, + "voteAccountAddress": { + "type": "string", + "description": "The address of the validator's vote account to which the stake will be delegated." + }, + "stakeAuthority": { + "type": "string", + "description": "The address of the account which has signing authority over staking operations on the stake account. Signs the transaction." + }, + "nonceOptions": { + "$ref": "#/definitions/v1alpha1NonceOptions", + "description": "The parameters required to use a durable transaction nonce for Solana transactions." + } + }, + "description": "The parameters required for the delegate stake action on Solana.", + "title": "Solana: Delegate Stake Parameters", + "required": [ + "stakeAccountAddress", + "voteAccountAddress", + "stakeAuthority" + ] + }, + "v1alpha1SolanaMergeStakeParameters": { + "type": "object", + "properties": { + "stakeAccountAddress": { + "type": "string", + "description": "The address of the stake account will be merged into and have stake added to it." + }, + "sourceStakeAccountAddress": { + "type": "string", + "description": "The address of the source stake account which will have no longer exist after the merge." + }, + "stakeAuthority": { + "type": "string", + "description": "The address of the account which has signing authority over staking operations on the stake account. Signs the transaction." + }, + "nonceOptions": { + "$ref": "#/definitions/v1alpha1NonceOptions", + "description": "The parameters required to use a durable transaction nonce for Solana transactions." + } + }, + "description": "The parameters required for merge stake action on Solana.", + "title": "Solana: Merge Stake Parameters", + "required": [ + "stakeAccountAddress", + "sourceStakeAccountAddress", + "stakeAuthority" + ] + }, + "v1alpha1SolanaSplitStakeParameters": { + "type": "object", + "properties": { + "stakeAccountAddress": { + "type": "string", + "description": "The address of the stake account will be split and have its stake removed." + }, + "newStakeAccountAddress": { + "type": "string", + "description": "The address of the new stake account which will be created and have the stake added to it." + }, + "stakeAuthority": { + "type": "string", + "description": "The address of the account which has signing authority over staking operations on the stake account. Signs the transaction." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount of stake to split, in Lamports." + }, + "nonceOptions": { + "$ref": "#/definitions/v1alpha1NonceOptions", + "description": "The parameters required to use a durable transaction nonce for Solana transactions." + } + }, + "description": "The parameters required for split stake action on Solana.", + "title": "Solana: Split Stake Parameters", + "required": [ + "stakeAccountAddress", + "newStakeAccountAddress", + "stakeAuthority", + "amount" + ] + }, + "v1alpha1SolanaStakingParameters": { + "type": "object", + "properties": { + "createStakeParameters": { + "$ref": "#/definitions/v1alpha1SolanaCreateStakeAccountParameters", + "description": "The parameters for create stake account action on Solana." + }, + "delegateStakeParameters": { + "$ref": "#/definitions/v1alpha1SolanaDelegateStakeParameters", + "description": "The parameters for delegate stake action on Solana." + }, + "deactivateStakeParameters": { + "$ref": "#/definitions/v1alpha1SolanaDeactivateStakeParameters", + "description": "The parameters for deactivate stake action on Solana." + }, + "withdrawStakeParameters": { + "$ref": "#/definitions/v1alpha1SolanaWithdrawStakeParameters", + "description": "The parameters for withdraw stake action on Solana." + }, + "splitStakeParameters": { + "$ref": "#/definitions/v1alpha1SolanaSplitStakeParameters", + "description": "The parameters for split stake action on Solana." + }, + "mergeStakeParameters": { + "$ref": "#/definitions/v1alpha1SolanaMergeStakeParameters", + "description": "The parameters for merge stake action on Solana." + } + }, + "description": "The parameters needed for staking on Solana.", + "title": "Solana: Staking Parameters" + }, + "v1alpha1SolanaWithdrawStakeParameters": { + "type": "object", + "properties": { + "stakeAccountAddress": { + "type": "string", + "description": "The address of the stake account from which stake will be withdrawn. Stake must be currently inactive." + }, + "recipientAddress": { + "type": "string", + "description": "The address of the recipient account which will receive the withdrawn stake." + }, + "withdrawAuthority": { + "type": "string", + "description": "The address of the account which has signing authority over withdrawing inactive stake from the stake account. Signs the transaction." + }, + "amount": { + "$ref": "#/definitions/v1alpha1Amount", + "description": "The amount to withdraw from the stake account, in Lamports." + }, + "nonceOptions": { + "$ref": "#/definitions/v1alpha1NonceOptions", + "description": "The parameters required to use a durable transaction nonce for Solana transactions." + } + }, + "description": "The parameters required for the withdraw stake action on Solana.", + "title": "Solana: Withdraw Stake Parameters", + "required": [ + "stakeAccountAddress", + "recipientAddress", + "withdrawAuthority", + "amount" + ] + }, + "v1alpha1StakingTarget": { + "type": "object", + "properties": { + "validator": { + "$ref": "#/definitions/v1alpha1Validator", + "description": "A validator to stake to." + }, + "contract": { + "$ref": "#/definitions/v1alpha1Contract", + "description": "A contract to send a staking action to." + } + }, + "description": "A Staking Target represents a destination that you perform an action on related to staking." + }, + "v1alpha1TxStepOutput": { + "type": "object", + "properties": { + "unsignedTx": { + "type": "string", + "description": "The unsigned transaction which was signed in order to be broadcasted.", + "readOnly": true + }, + "signedTx": { + "type": "string", + "description": "The signed transaction which was asked to be broadcasted.", + "readOnly": true + }, + "txHash": { + "type": "string", + "description": "The hash of the broadcasted transaction.", + "readOnly": true + }, + "state": { + "$ref": "#/definitions/v1alpha1TxStepOutputState", + "description": "The state of the transaction step.", + "readOnly": true + }, + "errorMessage": { + "type": "string", + "description": "The error message if the transaction step failed.", + "readOnly": true + } + }, + "description": "The details of a transaction being constructed and broadcasted to the network." + }, + "v1alpha1TxStepOutputState": { + "type": "string", + "enum": [ + "STATE_UNSPECIFIED", + "STATE_NOT_CONSTRUCTED", + "STATE_CONSTRUCTED", + "STATE_PENDING_SIGNING", + "STATE_SIGNED", + "STATE_BROADCASTING", + "STATE_CONFIRMING", + "STATE_CONFIRMED", + "STATE_FINALIZED", + "STATE_FAILED", + "STATE_SUCCESS", + "STATE_CANCELING", + "STATE_CANCELED", + "STATE_CANCEL_FAILED", + "STATE_FAILED_REFRESHABLE", + "STATE_REFRESHING", + "STATE_PENDING_EXT_BROADCAST" + ], + "default": "STATE_UNSPECIFIED", + "description": "State defines an enumeration of states for a staking transaction.\n\n - STATE_UNSPECIFIED: Unspecified transaction state, this is for backwards compatibility.\n - STATE_NOT_CONSTRUCTED: Tx has not yet been constructed in the backend.\n - STATE_CONSTRUCTED: Tx construction is over in the backend.\n - STATE_PENDING_SIGNING: Tx is waiting to be signed.\n - STATE_SIGNED: Tx has been signed and returned to the backend.\n - STATE_BROADCASTING: Tx is being broadcasted to the network.\n - STATE_CONFIRMING: Tx is waiting for confirmation.\n - STATE_CONFIRMED: Tx has been confirmed to be included in a block.\n - STATE_FINALIZED: Tx has been finalized.\n - STATE_FAILED: Tx construction or broadcasting failed.\n - STATE_SUCCESS: Tx has been successfully executed.\n - STATE_CANCELING: Tx is being canceled.\n - STATE_CANCELED: Tx has been canceled.\n - STATE_CANCEL_FAILED: Tx cancellation failed.\n - STATE_FAILED_REFRESHABLE: Tx failed but can be refreshed.\n - STATE_REFRESHING: Tx is being refreshed.\n - STATE_PENDING_EXT_BROADCAST: Tx is waiting to be externally broadcasted by the customer." + }, + "v1alpha1Validator": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "The resource name of the Validator.\nFormat: protocols/{protocolName}/networks/{networkName}/stakingTargets/{validatorName}\nEx: protocols/polygon/networks/goerli/stakingTargets/0x857679d69fE50E7B722f94aCd2629d80C355163d" + }, + "address": { + "type": "string", + "description": "The public address of the validator." + }, + "commissionRate": { + "type": "number", + "format": "float", + "title": "The rate of commission for the validator" + } + }, + "description": "A Validator resource represents an active validator for the given protocol network." + }, + "v1alpha1ViewStakingContextResponse": { + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "The address you are getting a staking context for." + }, + "ethereumKilnStakingContextDetails": { + "$ref": "#/definitions/v1alpha1EthereumKilnStakingContextDetails", + "description": "EthereumKiln staking context details." + } + }, + "description": "The response message for the ViewStakingContext request.", + "required": [ + "address", + "ethereumKilnStakingContextDetails" + ] + }, + "v1alpha1WaitStepOutput": { + "type": "object", + "properties": { + "start": { + "type": "string", + "format": "int64", + "description": "The beginning of wait period.", + "readOnly": true + }, + "current": { + "type": "string", + "format": "int64", + "description": "The current wait progress.", + "readOnly": true + }, + "target": { + "type": "string", + "format": "int64", + "description": "The target wait end point.", + "readOnly": true + }, + "unit": { + "$ref": "#/definitions/WaitStepOutputWaitUnit", + "description": "The wait unit (like checkpoint, block, epoch etc).", + "readOnly": true + }, + "state": { + "$ref": "#/definitions/v1alpha1WaitStepOutputState", + "description": "The state of the wait step.", + "readOnly": true + } + }, + "description": "The output details of a step where we wait for some kind of on-chain activity to finish like reaching a certain checkpoint, epoch or block." + }, + "v1alpha1WaitStepOutputState": { + "type": "string", + "enum": [ + "STATE_UNSPECIFIED", + "STATE_NOT_STARTED", + "STATE_IN_PROGRESS", + "STATE_COMPLETED" + ], + "default": "STATE_UNSPECIFIED", + "description": "WaitStepState defines an enumeration of states for a wait step.\n\n - STATE_UNSPECIFIED: Unspecified wait step state.\n - STATE_NOT_STARTED: Wait step has not started.\n - STATE_IN_PROGRESS: Wait step is in-progress.\n - STATE_COMPLETED: Wait step completed." + }, + "v1alpha1Workflow": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "The resource name of the workflow.\nFormat: projects/{projectUUID}/workflows/{workflowUUID}\nEx: projects/ 123e4567-e89b-12d3-a456-426614174000/workflows/123e4567-e89b-12d3-a456-426614174000", + "readOnly": true + }, + "action": { + "type": "string", + "title": "The resource name of the action being\nperformed.\nFormat: protocols/{protocol}/networks/{network}/actions/{action}" + }, + "polygonStakingParameters": { + "$ref": "#/definitions/v1alpha1PolygonStakingParameters", + "description": "Polygon staking parameters." + }, + "solanaStakingParameters": { + "$ref": "#/definitions/v1alpha1SolanaStakingParameters", + "description": "Solana staking parameters." + }, + "ethereumKilnStakingParameters": { + "$ref": "#/definitions/v1alpha1EthereumKilnStakingParameters", + "description": "EthereumKiln staking parameters." + }, + "state": { + "$ref": "#/definitions/v1alpha1WorkflowState", + "description": "The current state of the workflow.", + "readOnly": true + }, + "currentStepId": { + "type": "integer", + "format": "int32", + "description": "The index of the current step.", + "readOnly": true + }, + "steps": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1alpha1WorkflowStep" + }, + "description": "The list of steps for this workflow.", + "readOnly": true + }, + "createTime": { + "type": "string", + "format": "date-time", + "description": "The timestamp the workflow was created.", + "readOnly": true + }, + "updateTime": { + "type": "string", + "format": "date-time", + "description": "The timestamp the workflow was last updated.", + "readOnly": true + }, + "skipBroadcast": { + "type": "boolean", + "description": "Flag to skip tx broadcast to network on behalf of the user. Use this flag if you instead prefer to broadcast signed txs on your own." + }, + "completeTime": { + "type": "string", + "format": "date-time", + "description": "The timestamp the workflow completed.", + "readOnly": true + } + }, + "description": "A Workflow resource.", + "required": [ + "action", + "polygonStakingParameters", + "solanaStakingParameters", + "ethereumKilnStakingParameters" + ] + }, + "v1alpha1WorkflowState": { + "type": "string", + "enum": [ + "STATE_UNSPECIFIED", + "STATE_IN_PROGRESS", + "STATE_WAITING_FOR_SIGNING", + "STATE_COMPLETED", + "STATE_FAILED", + "STATE_CANCELING", + "STATE_CANCELED", + "STATE_CANCEL_FAILED", + "STATE_FAILED_REFRESHABLE", + "STATE_WAITING_FOR_EXT_BROADCAST" + ], + "default": "STATE_UNSPECIFIED", + "description": "- STATE_UNSPECIFIED: Unspecified workflow state, this is for backwards compatibility.\n - STATE_IN_PROGRESS: In Progress represents a workflow that is currently in progress.\n - STATE_WAITING_FOR_SIGNING: Waiting for signing represents the workflow is waiting on the consumer to sign and return the corresponding signed tx.\n - STATE_COMPLETED: Completed represents the workflow has completed.\n - STATE_FAILED: Failed represents the workflow has failed.\n - STATE_CANCELING: Canceling represents the workflow is being canceled.\n - STATE_CANCELED: Canceled represents the workflow has been canceled.\n - STATE_CANCEL_FAILED: Cancel failed represents the workflow failed to cancel.\n - STATE_FAILED_REFRESHABLE: Failed refreshable represents the workflow failed but can be refreshed.\n - STATE_WAITING_FOR_EXT_BROADCAST: Waiting for external broadcast represents the workflow is waiting for the customer to broadcast a tx and return its corresponding tx hash.", + "title": "The state of a workflow\nExample workflow states:\nIN_PROGRESS -\u003e WAITING_FOR_SIGNING -\u003e IN_PROGRESS -\u003e COMPLETED\n.................................................|-\u003e FAILED\nIN_PROGRESS -\u003e WAITING_FOR_SIGNING -\u003e CANCELING -\u003e CANCELED\n...............................................|-\u003e CANCEL_FAILED" + }, + "v1alpha1WorkflowStep": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The human readable name of the step.", + "readOnly": true + }, + "txStepOutput": { + "$ref": "#/definitions/v1alpha1TxStepOutput", + "description": "The tx step output (e.g. transaction metadata such as unsigned tx, signed tx etc).", + "readOnly": true + }, + "waitStepOutput": { + "$ref": "#/definitions/v1alpha1WaitStepOutput", + "description": "The waiting details for any kind like how many checkpoints away for unbonding etc.", + "readOnly": true + } + }, + "description": "The information for a step in the workflow.", + "title": "The information for a step in the workflow" + } + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f738c69 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2394 @@ +{ + "name": "@coinbase/staking-client-library-ts", + "version": "0.4.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@coinbase/staking-client-library-ts", + "version": "0.4.2", + "license": "Apache-2.0", + "dependencies": { + "@ethereumjs/tx": "^5.1.0", + "node-jose": "^2.2.0" + }, + "devDependencies": { + "@types/node-jose": "^1.1.10", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.3", + "rimraf": "^3.0.2", + "typescript": "^5.2.2" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@ethereumjs/common": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.1.0.tgz", + "integrity": "sha512-XWdQvUjlQHVwh4uGEPFKHpsic69GOsMXEhlHrggS5ju/+2zAmmlz6B25TkCCymeElC9DUp13tH5Tc25Iuvtlcg==", + "dependencies": { + "@ethereumjs/util": "^9.0.1", + "crc": "^4.3.2" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.1.tgz", + "integrity": "sha512-Ab/Hfzz+T9Zl+65Nkg+9xAmwKPLicsnQ4NW49pgvJp9ovefuic95cgOS9CbPc9izIEgsqm1UitV0uNveCvud9w==", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.1.0.tgz", + "integrity": "sha512-VUhw2+4yXArJZRWhPjmZFrN4WUjUo0qUZUszVpW2KzsGlqCFf67kwJcH9Rca5eS0CRHjr2qHJLpvYOjNuaXVdA==", + "dependencies": { + "@ethereumjs/common": "^4.1.0", + "@ethereumjs/rlp": "^5.0.1", + "@ethereumjs/util": "^9.0.1", + "ethereum-cryptography": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@ethereumjs/util": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.0.1.tgz", + "integrity": "sha512-NdFFEzCc3H1sYkNnnySwLg6owdQMhjUc2jfuDyx8Xv162WSluCnnSKouKOSG3njGNEyy2I9NmF8zTRDwuqpZWA==", + "dependencies": { + "@ethereumjs/rlp": "^5.0.1", + "ethereum-cryptography": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@scure/base": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.5.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.6.tgz", + "integrity": "sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==", + "dev": true + }, + "node_modules/@types/node-jose": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@types/node-jose/-/node-jose-1.1.10.tgz", + "integrity": "sha512-7L0ucJTugW4x/sYpQ+c5IudAwr0pFuxDVnZLpHKWpff7p1lVa3wTuNvnrzFBNeLojE+UY0cVCwNGXLxXsMIrzw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/crc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", + "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "buffer": ">=6.0.3" + }, + "peerDependenciesMeta": { + "buffer": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", + "@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" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "dev": true, + "dependencies": { + "flatted": "^3.2.7", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-jose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz", + "integrity": "sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw==", + "dependencies": { + "base64url": "^3.0.1", + "buffer": "^6.0.3", + "es6-promise": "^4.2.8", + "lodash": "^4.17.21", + "long": "^5.2.0", + "node-forge": "^1.2.1", + "pako": "^2.0.4", + "process": "^0.11.10", + "uuid": "^9.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1b84bc1 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "@coinbase/staking-client-library-ts", + "version": "0.4.2", + "description": "Coinbase Cloud Staking API Typescript Library", + "repository": "https://github.com/coinbase/staking-client-library-ts.git", + "license": "Apache-2.0", + "scripts": { + "clean": "rimraf ./dist", + "build": "npm run clean && tsc", + "prepare": "npm run build", + "lint": "eslint . --ext .ts --ignore-pattern '/dist/*/*'", + "lintfix": "eslint . --ext .ts --fix --ignore-pattern '/dist/*/*'" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, + "files": [ + "src/", + "dist/" + ], + "dependencies": { + "node-jose": "^2.2.0", + "@ethereumjs/tx": "^5.1.0" + }, + "devDependencies": { + "@types/node-jose": "^1.1.10", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.3", + "typescript": "^5.2.2", + "rimraf": "^3.0.2" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts" +} diff --git a/src/auth/index.ts b/src/auth/index.ts new file mode 100644 index 0000000..ccb2171 --- /dev/null +++ b/src/auth/index.ts @@ -0,0 +1,173 @@ +import { readFileSync } from "fs"; +import { JWK, JWS } from "node-jose"; + +const legacyPemHeader = "-----BEGIN ECDSA Private Key-----"; +const legacyPemFooter = "-----END ECDSA Private Key-----"; +const pemHeader = "-----BEGIN EC PRIVATE KEY-----"; +const pemFooter = "-----END EC PRIVATE KEY-----"; + +/** + * Build a JWT for the specified service and URI. + * @param service The name of the service. + * @param uri The URI for which the JWT is to be generated. + * @returns The generated JWT. + */ +export const buildJWT = async ( + url: string, + method = "GET", +): Promise => { + const keyFile = readFileSync(".coinbase_cloud_api_key.json", { + encoding: "utf8", + }); + const apiKey: APIKey = JSON.parse(keyFile); + + const pemPrivateKey = extractPemKey(apiKey.privateKey); + let privateKey: JWK.Key; + + try { + privateKey = await JWK.asKey(pemPrivateKey, "pem"); + if (privateKey.kty !== "EC") { + throw new Error("Not an EC private key"); + } + } catch (error) { + throw new Error(`jwt: Could not decode or parse private key. ${error}`); + } + + const header = { + alg: "ES256", + kid: apiKey.name, + typ: "JWT", + nonce: nonce(), + }; + + const audience = getAudience(url); + const uri = `${method} ${url.substring(8)}`; + + const claims: APIKeyClaims = { + sub: apiKey.name, + iss: "coinbase-cloud", + nbf: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 60, // +1 minute + aud: [audience], + uri, + }; + + const payload = Buffer.from(JSON.stringify(claims)).toString("utf8"); + + try { + const result = await JWS.createSign( + { format: "compact", fields: header }, + privateKey, + ) + .update(payload) + .final(); + + return result as unknown as string; + } catch (err) { + throw new Error(`jwt: Failed to sign JWT. ${err}`); + } +}; + +/** + * Represents the API key details. + */ +interface APIKey { + /** The name of the API key. */ + name: string; + /** The private key string. */ + privateKey: string; +} + +/** + * Represents the claims included in the JWT. + */ +interface APIKeyClaims { + /** Audience of the JWT. */ + aud: string[]; + /** Subject of the JWT. Typically the identifier of the API key. */ + sub: string; + /** Expiry time of the JWT in seconds. */ + exp: number; + /** Time before which the JWT is not valid in seconds. */ + nbf: number; + /** Issuer of the JWT. */ + iss: string; + /** URI claim for the JWT. */ + uri: string; +} + +/** + * Extracts the PEM key from the given string. + * @param privateKeyString The string for the private key from which to extract the PEM key. + * @returns The extracted PEM key body. + */ +const extractPemKey = (privateKeyString: string): string => { + // Remove all newline characters + privateKeyString = privateKeyString.replace(/\n/g, ""); + + // If the string starts with the standard PEM header and footer, return as is. + if ( + privateKeyString.startsWith(pemHeader) && + privateKeyString.endsWith(pemFooter) + ) { + return privateKeyString; + } + + // If the string starts with the legacy header and footer, replace them. + const regex = new RegExp( + `^${legacyPemHeader}([\\s\\S]+?)${legacyPemFooter}$`, + ); + + const match = privateKeyString.match(regex); + if (match && match[1]) { + return pemHeader + match[1].trim() + pemFooter; + } + + // The string does not match any of the expected formats. + throw new Error("wrong format of API private key"); +}; + +/** + * Generates a nonce of 16 numeric characters. + * @returns The generated nonce. + */ +const nonce = (): string => { + const range = "0123456789"; + let result = ""; + for (let i = 0; i < 16; i++) { + result += range.charAt(Math.floor(Math.random() * range.length)); + } + + return result; +}; + +const getAudience = (url: string): string => { + if (url.indexOf("staking") > -1) { + return "staking"; + } else if (url.indexOf("rewards") > -1) { + return "rewards-reporting"; + } else { + return "unknown"; + } +}; + +export const customFetch = async ( + input: RequestInfo | URL, + init?: RequestInit | undefined, +): Promise => { + // remove query parameters + let url = input.toString(); + if (url.indexOf("?") > -1) { + url = url.substring(0, url.indexOf("?")); + } + const token = await buildJWT(url, init?.method); + const params = { + ...init, + headers: { + ...init?.headers, + Authorization: `Bearer ${token}`, + }, + }; + + return fetch(input, params); +}; diff --git a/src/client/protocols/ethereum_kiln_staking.ts b/src/client/protocols/ethereum_kiln_staking.ts new file mode 100644 index 0000000..c9b4630 --- /dev/null +++ b/src/client/protocols/ethereum_kiln_staking.ts @@ -0,0 +1,115 @@ +import { StakingServiceClient } from "../staking-service-client"; +import { + CreateWorkflowRequest, + Workflow, +} from "../../gen/coinbase/staking/v1alpha1/workflow.pb"; +import { + ViewStakingContextRequest, + ViewStakingContextResponse, +} from "../../gen/coinbase/staking/v1alpha1/staking_context.pb"; + +export class EthereumKiln { + private parent: StakingServiceClient; + + constructor(parent: StakingServiceClient) { + this.parent = parent; + } + + async stake( + projectId: string, + network: string, + skipBroadcast: boolean = false, + stakerAddress: string, + integratorContractAddress: string, + amount: string, + ): Promise { + const req: CreateWorkflowRequest = { + parent: `projects/${projectId}`, + workflow: { + action: `protocols/ethereum_kiln/networks/${network}/actions/stake`, + skipBroadcast: skipBroadcast, + ethereumKilnStakingParameters: { + stakeParameters: { + stakerAddress: stakerAddress, + integratorContractAddress: integratorContractAddress, + amount: { + value: amount, + currency: "ETH", + }, + }, + }, + }, + }; + + return this.parent.createWorkflow(projectId, req); + } + + async unstake( + projectId: string, + network: string, + skipBroadcast: boolean = false, + stakerAddress: string, + integratorContractAddress: string, + amount: string, + ): Promise { + const req: CreateWorkflowRequest = { + parent: `projects/${projectId}`, + workflow: { + action: `protocols/ethereum_kiln/networks/${network}/actions/unstake`, + skipBroadcast: skipBroadcast, + ethereumKilnStakingParameters: { + unstakeParameters: { + stakerAddress: stakerAddress, + integratorContractAddress: integratorContractAddress, + amount: { + value: amount, + currency: "ETH", + }, + }, + }, + }, + }; + + return this.parent.createWorkflow(projectId, req); + } + + async claimStake( + projectId: string, + network: string, + skipBroadcast: boolean = false, + stakerAddress: string, + integratorContractAddress: string, + ): Promise { + const req: CreateWorkflowRequest = { + parent: `projects/${projectId}`, + workflow: { + action: `protocols/ethereum_kiln/networks/${network}/actions/claim_stake`, + skipBroadcast: skipBroadcast, + ethereumKilnStakingParameters: { + claimStakeParameters: { + stakerAddress: stakerAddress, + integratorContractAddress: integratorContractAddress, + }, + }, + }, + }; + + return this.parent.createWorkflow(projectId, req); + } + + async viewStakingContext( + address: string, + network: string, + integratorContractAddress: string, + ): Promise { + const req: ViewStakingContextRequest = { + address: address, + network: `protocols/ethereum_kiln/networks/${network}`, + ethereumKilnStakingContextParameters: { + integratorContractAddress: integratorContractAddress, + }, + }; + + return this.parent.viewStakingContext(req); + } +} diff --git a/src/client/staking-service-client.ts b/src/client/staking-service-client.ts new file mode 100644 index 0000000..d0cd6cb --- /dev/null +++ b/src/client/staking-service-client.ts @@ -0,0 +1,262 @@ +import { StakingService } from "../gen/coinbase/staking/v1alpha1/api.pb"; +import { + ListProtocolsRequest, + ListProtocolsResponse, +} from "../gen/coinbase/staking/v1alpha1/protocol.pb"; +import { + ListNetworksRequest, + ListNetworksResponse, +} from "../gen/coinbase/staking/v1alpha1/network.pb"; +import { + ListActionsRequest, + ListActionsResponse, +} from "../gen/coinbase/staking/v1alpha1/action.pb"; +import * as fm from "../gen/fetch.pb"; +import { buildJWT } from "../auth"; +import { + ViewStakingContextRequest, + ViewStakingContextResponse, +} from "../gen/coinbase/staking/v1alpha1/staking_context.pb"; +import { + CreateWorkflowRequest, + GetWorkflowRequest, + ListWorkflowsRequest, + ListWorkflowsResponse, + PerformWorkflowStepRequest, + RefreshWorkflowStepRequest, + Workflow, + WorkflowState, + WorkflowStep, + TxStepOutput, + WaitStepOutput, +} from "../gen/coinbase/staking/v1alpha1/workflow.pb"; + +import { EthereumKiln } from "./protocols/ethereum_kiln_staking"; + +const DEFAULT_URL = "https://api.developer.coinbase.com/staking"; + +export class StakingServiceClient { + readonly baseURL: string; + readonly EthereumKiln: EthereumKiln; + + constructor(baseURL?: string) { + if (baseURL) { + this.baseURL = baseURL; + } else { + this.baseURL = DEFAULT_URL; + } + + this.EthereumKiln = new EthereumKiln(this); + } + + // List protocols supported by Staking API + async listProtocols(): Promise { + const path: string = "/api/v1alpha1/protocols"; + const method: string = "GET"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: ListProtocolsRequest = {}; + + return StakingService.ListProtocols(req, initReq); + } + + // List networks supported by Staking API for a given protocol. + async listNetworks(protocol: string): Promise { + const parent: string = `protocols/${protocol}`; + const path: string = `/api/v1alpha1/${parent}/networks`; + const method: string = "GET"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: ListNetworksRequest = { + parent: parent, + }; + + return StakingService.ListNetworks(req, initReq); + } + + // List actions supported by Staking API for a given protocol and network. + async listActions( + protocol: string, + network: string, + ): Promise { + const parent: string = `protocols/${protocol}/networks/${network}`; + const path: string = `/api/v1alpha1/${parent}/actions`; + const method: string = "GET"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: ListActionsRequest = { + parent: parent, + }; + + return StakingService.ListActions(req, initReq); + } + + // Returns point-in-time context of staking data for an address. This function takes the entire req object as input. + // Use the protocol-specific helper functions like EthereumKiln.ViewStakingContext to view protocol and network + // specific staking context. + async viewStakingContext( + req: ViewStakingContextRequest, + ): Promise { + const path: string = "/api/v1alpha1/viewStakingContext:view"; + const method: string = "GET"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + return StakingService.ViewStakingContext(req, initReq); + } + + // Create a workflow under a given project. This function takes the entire req object as input. + // Use the protocol-specific helper functions like EthereumKiln.Stake to create a protocol and action specific workflow. + async createWorkflow( + projectId: string, + req: CreateWorkflowRequest, + ): Promise { + const parent: string = `projects/${projectId}`; + const path: string = `/api/v1alpha1/${parent}/workflows`; + const method: string = "POST"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + return StakingService.CreateWorkflow(req, initReq); + } + + // Get a workflow given its project and workflow id. + async getWorkflow(projectId: string, workflowId: string): Promise { + const parent: string = `projects/${projectId}`; + const name: string = `${parent}/workflows/${workflowId}`; + const path: string = `/api/v1alpha1/${name}`; + const method: string = "GET"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: GetWorkflowRequest = { + name: name, + }; + + return StakingService.GetWorkflow(req, initReq); + } + + // Return back a signed tx or a broadcasted tx hash for a given workflow and step number. + async performWorkflowStep( + projectId: string, + workflowId: string, + stepIndex: number, + data: string, + ): Promise { + const parent: string = `projects/${projectId}`; + const name: string = `${parent}/workflows/${workflowId}`; + const path: string = `/api/v1alpha1/${name}/step`; + const method: string = "POST"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: PerformWorkflowStepRequest = { + name: name, + step: stepIndex, + data, + }; + + return StakingService.PerformWorkflowStep(req, initReq); + } + + // Refresh a workflow step given its workflow name and step number. + async refreshWorkflowStep( + projectId: string, + workflowId: string, + stepIndex: number, + ): Promise { + const parent: string = `projects/${projectId}`; + const name: string = `${parent}/workflows/${workflowId}`; + const path: string = `/api/v1alpha1/${name}/refresh`; + const method: string = "POST"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: RefreshWorkflowStepRequest = { + name: name, + step: stepIndex, + }; + + return StakingService.RefreshWorkflowStep(req, initReq); + } + + // List workflows for a given project. + async listWorkflows( + project: string, + pageSize: number = 100, + filter: string = "", + ): Promise { + const parent: string = `projects/${project}`; + const path: string = `/api/v1alpha1/${parent}/workflows`; + const method: string = "GET"; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(this.baseURL, path, method); + + const req: ListWorkflowsRequest = { + parent: parent, + pageSize: pageSize, + filter: filter, + }; + + return StakingService.ListWorkflows(req, initReq); + } +} + +export function workflowHasFinished(workflow: Workflow): boolean { + return ( + workflow.state === WorkflowState.STATE_COMPLETED || + workflow.state === WorkflowState.STATE_FAILED || + workflow.state === WorkflowState.STATE_CANCELED || + workflow.state === WorkflowState.STATE_CANCEL_FAILED + ); +} + +export function workflowWaitingForSigning(workflow: Workflow): boolean { + return workflow.state === WorkflowState.STATE_WAITING_FOR_SIGNING; +} + +export function workflowWaitingForExternalBroadcast( + workflow: Workflow, +): boolean { + return workflow.state === WorkflowState.STATE_WAITING_FOR_EXT_BROADCAST; +} + +export function workflowFailedRefreshable(workflow: Workflow): boolean { + return workflow.state === WorkflowState.STATE_FAILED_REFRESHABLE; +} + +export function isTxStepOutput(step: WorkflowStep): step is WorkflowStep & { txStepOutput: TxStepOutput } { + return (step as WorkflowStep).txStepOutput !== undefined; +} + +export function isWaitStepOutput(step: WorkflowStep): step is WorkflowStep & { waitStepOutput: WaitStepOutput } { + return (step as WorkflowStep).waitStepOutput !== undefined; +} + +async function getAuthDetails( + baseURL: string, + path: string, + method: string, +): Promise { + // Generate the JWT token + const token = await buildJWT(baseURL + path, method); + + return { + pathPrefix: baseURL, + headers: { + Authorization: `Bearer ${token}`, + }, + }; +} diff --git a/src/examples/public/example.ts b/src/examples/public/example.ts new file mode 100644 index 0000000..2497318 --- /dev/null +++ b/src/examples/public/example.ts @@ -0,0 +1,15 @@ +import { StakingServiceClient } from "../../client/staking-service-client"; + +const client = new StakingServiceClient(); + +client.listProtocols().then((response) => { + console.log(response); +}); + +client.listNetworks("ethereum_kiln").then((response) => { + console.log(response); +}); + +client.listActions("ethereum_kiln", "goerli").then((response) => { + console.log(response); +}); diff --git a/src/examples/public/example_e2e_staking.ts b/src/examples/public/example_e2e_staking.ts new file mode 100644 index 0000000..3e31f73 --- /dev/null +++ b/src/examples/public/example_e2e_staking.ts @@ -0,0 +1,166 @@ +import { TxSignerFactory } from "../../signers"; +import { + StakingServiceClient, + workflowHasFinished, + workflowWaitingForSigning, + workflowWaitingForExternalBroadcast, + isTxStepOutput, + isWaitStepOutput, +} from "../../client/staking-service-client"; +import { Workflow } from "../../gen/coinbase/staking/v1alpha1/workflow.pb"; +import { calculateTimeDifference } from "../../utils/date"; + +const projectId: string = ""; // replace with your project id +const privateKey: string = ""; // replace with your private key +const stakerAddress: string = ""; // replace with your staker address +const integrationAddress: string = "0x0a868e4e07a0a00587a783720b76fad9f7eea009"; // replace with your integration address +const amount: string = "123"; // replace with your amount +const network: string = "goerli"; // replace with your network + +const client = new StakingServiceClient(); + +const signer = TxSignerFactory.getSigner("ethereum"); + +async function stakePartialEth(): Promise { + if (projectId === "" || privateKey === "" || stakerAddress === "") { + throw new Error( + "Please set the projectId, privateKey and stakerAddress variables in this file", + ); + } + + let unsignedTx = ""; + let workflow: Workflow = {} as Workflow; + let currentStepId: number | undefined; + let workflowId: string; + + try { + // Create a new eth kiln stake workflow + workflow = await client.EthereumKiln.stake( + projectId, + network, + false, + stakerAddress, + integrationAddress, + amount, + ); + + workflowId = workflow.name?.split("/").pop() || ""; + if (workflowId == null || workflowId === "") { + throw new Error("Unexpected workflow state. workflowId is null"); + } + + currentStepId = workflow.currentStepId; + if (currentStepId == null) { + throw new Error("Unexpected workflow state. currentStepId is null"); + } + + console.log("Workflow created %s ...", workflow.name); + } catch (error: any) { + throw new Error(`Error creating workflow: ${error.message}`); + } + + // Loop until the workflow has reached an end state. + // eslint-disable-next-line no-constant-condition + while (true) { + // Every second, get the latest workflow state. + // If the workflow is waiting for signing, sign the unsigned tx and return back the signed tx. + // If the workflow is waiting for external broadcast, sign and broadcast the unsigned tx externally and return back the tx hash via the PerformWorkflowStep API. + // Note: In this example, we just log this message as the wallet provider needs to implement this logic. + try { + workflow = await client.getWorkflow(projectId, workflowId); + } catch (error: any) { + // TODO: add retry logic for network errors + throw new Error(`Error getting workflow: ${error.message}`); + } + + await printWorkflowProgressDetails(workflow); + + if (workflowWaitingForSigning(workflow)) { + unsignedTx = + workflow.steps![currentStepId].txStepOutput?.unsignedTx || ""; + if (unsignedTx === "") { + console.log("Waiting for unsigned tx to be available ..."); + await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second + continue; + } + + console.log("Signing unsigned tx %s ...", unsignedTx); + const signedTx = await signer.signTransaction(privateKey, unsignedTx); + console.log("Returning back signed tx %s ...", signedTx); + + workflow = await client.performWorkflowStep( + projectId, + workflowId, + currentStepId, + signedTx, + ); + } else if (workflowWaitingForExternalBroadcast(workflow)) { + console.log( + "Please sign and broadcast this unsigned tx %s externally and return back the tx hash via the PerformWorkflowStep API ...", + unsignedTx, + ); + break; + } else if (workflowHasFinished(workflow)) { + console.log("Workflow completed with state %s ...", workflow.state); + break; + } + + await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second + } +} + +async function printWorkflowProgressDetails(workflow: Workflow): Promise { + if (workflow.steps == null || workflow.steps.length === 0) { + console.log("Waiting for steps to be created ..."); + await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second + return; + } + + const currentStepId = workflow.currentStepId; + if (currentStepId == null) { + return; + } + + const step = workflow.steps[currentStepId]; + + let stepDetails = ""; + + if (isTxStepOutput(step)) { + stepDetails = `state: ${step.txStepOutput?.state} tx hash: ${step.txStepOutput?.txHash}`; + } else if (isWaitStepOutput(step)) { + stepDetails = `state: ${step.waitStepOutput?.state}} current: ${step.waitStepOutput?.current}} target: ${step.waitStepOutput?.target}` + } else { + throw new Error("Encountered unexpected workflow step type"); + } + + const runtime = calculateTimeDifference( + workflow.createTime, + workflow.updateTime, + ); + + if (workflowHasFinished(workflow)) { + console.log( + "Workflow reached end state - step name: %s %s workflow state: %s runtime: %d seconds", + step.name, + stepDetails, + workflow.state, + runtime, + ); + } else { + console.log( + "Waiting for workflow to finish - step name: %s %s workflow state: %s runtime: %d seconds", + step.name, + stepDetails, + workflow.state, + runtime, + ); + } +} + +stakePartialEth() + .then(() => { + console.log("Done staking eth"); + }) + .catch((error) => { + console.error("Error staking eth: ", error.message); + }); diff --git a/src/gen/coinbase/staking/v1alpha1/action.pb.ts b/src/gen/coinbase/staking/v1alpha1/action.pb.ts new file mode 100644 index 0000000..23514f2 --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/action.pb.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type Action = { + name?: string +} + +export type ListActionsRequest = { + parent?: string +} + +export type ListActionsResponse = { + actions?: Action[] +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/api.pb.ts b/src/gen/coinbase/staking/v1alpha1/api.pb.ts new file mode 100644 index 0000000..7eafe2d --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/api.pb.ts @@ -0,0 +1,45 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as fm from "../../../fetch.pb" +import * as CoinbaseStakingV1alpha1Action from "./action.pb" +import * as CoinbaseStakingV1alpha1Network from "./network.pb" +import * as CoinbaseStakingV1alpha1Protocol from "./protocol.pb" +import * as CoinbaseStakingV1alpha1Staking_context from "./staking_context.pb" +import * as CoinbaseStakingV1alpha1Staking_target from "./staking_target.pb" +import * as CoinbaseStakingV1alpha1Workflow from "./workflow.pb" +export class StakingService { + static ListProtocols(req: CoinbaseStakingV1alpha1Protocol.ListProtocolsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/protocols?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) + } + static ListNetworks(req: CoinbaseStakingV1alpha1Network.ListNetworksRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["parent"]}/networks?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static ListStakingTargets(req: CoinbaseStakingV1alpha1Staking_target.ListStakingTargetsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["parent"]}/stakingTargets?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static ListActions(req: CoinbaseStakingV1alpha1Action.ListActionsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["parent"]}/actions?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static CreateWorkflow(req: CoinbaseStakingV1alpha1Workflow.CreateWorkflowRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["parent"]}/workflows`, {...initReq, method: "POST", body: JSON.stringify(req["workflow"], fm.replacer)}) + } + static GetWorkflow(req: CoinbaseStakingV1alpha1Workflow.GetWorkflowRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["name"]}?${fm.renderURLSearchParams(req, ["name"])}`, {...initReq, method: "GET"}) + } + static ListWorkflows(req: CoinbaseStakingV1alpha1Workflow.ListWorkflowsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["parent"]}/workflows?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static PerformWorkflowStep(req: CoinbaseStakingV1alpha1Workflow.PerformWorkflowStepRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["name"]}/step`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)}) + } + static RefreshWorkflowStep(req: CoinbaseStakingV1alpha1Workflow.RefreshWorkflowStepRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/${req["name"]}/refresh`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)}) + } + static ViewStakingContext(req: CoinbaseStakingV1alpha1Staking_context.ViewStakingContextRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/api/v1alpha1/viewStakingContext:view?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) + } +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/common.pb.ts b/src/gen/coinbase/staking/v1alpha1/common.pb.ts new file mode 100644 index 0000000..32edc2b --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/common.pb.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type Amount = { + value?: string + currency?: string +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts b/src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts new file mode 100644 index 0000000..bcb0861 --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts @@ -0,0 +1,52 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as CoinbaseStakingV1alpha1Common from "./common.pb" + +type Absent = { [k in Exclude]?: undefined }; +type OneOf = + | { [k in keyof T]?: undefined } + | ( + keyof T extends infer K ? + (K extends string & keyof T ? { [k in K]: T[K] } & Absent + : never) + : never); +export type EthereumKilnStakeParameters = { + stakerAddress?: string + integratorContractAddress?: string + amount?: CoinbaseStakingV1alpha1Common.Amount +} + +export type EthereumKilnUnstakeParameters = { + stakerAddress?: string + integratorContractAddress?: string + amount?: CoinbaseStakingV1alpha1Common.Amount +} + +export type EthereumKilnClaimStakeParameters = { + stakerAddress?: string + integratorContractAddress?: string +} + + +type BaseEthereumKilnStakingParameters = { +} + +export type EthereumKilnStakingParameters = BaseEthereumKilnStakingParameters + & OneOf<{ stakeParameters: EthereumKilnStakeParameters; unstakeParameters: EthereumKilnUnstakeParameters; claimStakeParameters: EthereumKilnClaimStakeParameters }> + +export type EthereumKilnStakingContextParameters = { + integratorContractAddress?: string +} + +export type EthereumKilnStakingContextDetails = { + ethereumBalance?: CoinbaseStakingV1alpha1Common.Amount + integratorShareBalance?: CoinbaseStakingV1alpha1Common.Amount + integratorShareUnderlyingBalance?: CoinbaseStakingV1alpha1Common.Amount + totalExitableEth?: CoinbaseStakingV1alpha1Common.Amount + totalSharesPendingExit?: CoinbaseStakingV1alpha1Common.Amount + fulfillableShareCount?: CoinbaseStakingV1alpha1Common.Amount +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/network.pb.ts b/src/gen/coinbase/staking/v1alpha1/network.pb.ts new file mode 100644 index 0000000..be49cf0 --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/network.pb.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type Network = { + name?: string + isMainnet?: boolean +} + +export type ListNetworksRequest = { + parent?: string +} + +export type ListNetworksResponse = { + networks?: Network[] +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/polygon.pb.ts b/src/gen/coinbase/staking/v1alpha1/polygon.pb.ts new file mode 100644 index 0000000..41ad9ab --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/polygon.pb.ts @@ -0,0 +1,44 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as CoinbaseStakingV1alpha1Common from "./common.pb" + +type Absent = { [k in Exclude]?: undefined }; +type OneOf = + | { [k in keyof T]?: undefined } + | ( + keyof T extends infer K ? + (K extends string & keyof T ? { [k in K]: T[K] } & Absent + : never) + : never); +export type PolygonStakeParameters = { + delegatorAddress?: string + validatorAddress?: string + amount?: CoinbaseStakingV1alpha1Common.Amount +} + +export type PolygonUnstakeParameters = { + delegatorAddress?: string + validatorAddress?: string + amount?: CoinbaseStakingV1alpha1Common.Amount +} + +export type PolygonRestakeParameters = { + delegatorAddress?: string + validatorAddress?: string +} + +export type PolygonClaimRewardsParameters = { + delegatorAddress?: string + validatorAddress?: string +} + + +type BasePolygonStakingParameters = { +} + +export type PolygonStakingParameters = BasePolygonStakingParameters + & OneOf<{ stakeParameters: PolygonStakeParameters; unstakeParameters: PolygonUnstakeParameters; restakeParameters: PolygonRestakeParameters; claimRewardsParameters: PolygonClaimRewardsParameters }> \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/protocol.pb.ts b/src/gen/coinbase/staking/v1alpha1/protocol.pb.ts new file mode 100644 index 0000000..7c3c80b --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/protocol.pb.ts @@ -0,0 +1,15 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type Protocol = { + name?: string +} + +export type ListProtocolsRequest = { +} + +export type ListProtocolsResponse = { + protocols?: Protocol[] +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/solana.pb.ts b/src/gen/coinbase/staking/v1alpha1/solana.pb.ts new file mode 100644 index 0000000..41f0a26 --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/solana.pb.ts @@ -0,0 +1,73 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as CoinbaseStakingV1alpha1Common from "./common.pb" + +type Absent = { [k in Exclude]?: undefined }; +type OneOf = + | { [k in keyof T]?: undefined } + | ( + keyof T extends infer K ? + (K extends string & keyof T ? { [k in K]: T[K] } & Absent + : never) + : never); +export type NonceOptions = { + nonce?: string + nonceAccount?: string + nonceAuthority?: string +} + +export type SolanaCreateStakeAccountParameters = { + stakeAccountAddress?: string + fromAddress?: string + stakeAuthority?: string + withdrawAuthority?: string + amount?: CoinbaseStakingV1alpha1Common.Amount + nonceOptions?: NonceOptions +} + +export type SolanaDelegateStakeParameters = { + stakeAccountAddress?: string + voteAccountAddress?: string + stakeAuthority?: string + nonceOptions?: NonceOptions +} + +export type SolanaDeactivateStakeParameters = { + stakeAccountAddress?: string + stakeAuthority?: string + nonceOptions?: NonceOptions +} + +export type SolanaWithdrawStakeParameters = { + stakeAccountAddress?: string + recipientAddress?: string + withdrawAuthority?: string + amount?: CoinbaseStakingV1alpha1Common.Amount + nonceOptions?: NonceOptions +} + +export type SolanaSplitStakeParameters = { + stakeAccountAddress?: string + newStakeAccountAddress?: string + stakeAuthority?: string + amount?: CoinbaseStakingV1alpha1Common.Amount + nonceOptions?: NonceOptions +} + +export type SolanaMergeStakeParameters = { + stakeAccountAddress?: string + sourceStakeAccountAddress?: string + stakeAuthority?: string + nonceOptions?: NonceOptions +} + + +type BaseSolanaStakingParameters = { +} + +export type SolanaStakingParameters = BaseSolanaStakingParameters + & OneOf<{ createStakeParameters: SolanaCreateStakeAccountParameters; delegateStakeParameters: SolanaDelegateStakeParameters; deactivateStakeParameters: SolanaDeactivateStakeParameters; withdrawStakeParameters: SolanaWithdrawStakeParameters; splitStakeParameters: SolanaSplitStakeParameters; mergeStakeParameters: SolanaMergeStakeParameters }> \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts b/src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts new file mode 100644 index 0000000..ebc8eb6 --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts @@ -0,0 +1,32 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as CoinbaseStakingV1alpha1Ethereum_kiln from "./ethereum_kiln.pb" + +type Absent = { [k in Exclude]?: undefined }; +type OneOf = + | { [k in keyof T]?: undefined } + | ( + keyof T extends infer K ? + (K extends string & keyof T ? { [k in K]: T[K] } & Absent + : never) + : never); + +type BaseViewStakingContextRequest = { + address?: string + network?: string +} + +export type ViewStakingContextRequest = BaseViewStakingContextRequest + & OneOf<{ ethereumKilnStakingContextParameters: CoinbaseStakingV1alpha1Ethereum_kiln.EthereumKilnStakingContextParameters }> + + +type BaseViewStakingContextResponse = { + address?: string +} + +export type ViewStakingContextResponse = BaseViewStakingContextResponse + & OneOf<{ ethereumKilnStakingContextDetails: CoinbaseStakingV1alpha1Ethereum_kiln.EthereumKilnStakingContextDetails }> \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/staking_target.pb.ts b/src/gen/coinbase/staking/v1alpha1/staking_target.pb.ts new file mode 100644 index 0000000..80196ac --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/staking_target.pb.ts @@ -0,0 +1,42 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +type Absent = { [k in Exclude]?: undefined }; +type OneOf = + | { [k in keyof T]?: undefined } + | ( + keyof T extends infer K ? + (K extends string & keyof T ? { [k in K]: T[K] } & Absent + : never) + : never); +export type Validator = { + name?: string + address?: string + commissionRate?: number +} + +export type Contract = { + name?: string + address?: string +} + + +type BaseStakingTarget = { +} + +export type StakingTarget = BaseStakingTarget + & OneOf<{ validator: Validator; contract: Contract }> + +export type ListStakingTargetsRequest = { + parent?: string + pageSize?: number + pageToken?: string +} + +export type ListStakingTargetsResponse = { + stakingTargets?: StakingTarget[] + nextPageToken?: string +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/workflow.pb.ts b/src/gen/coinbase/staking/v1alpha1/workflow.pb.ts new file mode 100644 index 0000000..cc0a898 --- /dev/null +++ b/src/gen/coinbase/staking/v1alpha1/workflow.pb.ts @@ -0,0 +1,139 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as GoogleProtobufTimestamp from "../../../google/protobuf/timestamp.pb" +import * as CoinbaseStakingV1alpha1Ethereum_kiln from "./ethereum_kiln.pb" +import * as CoinbaseStakingV1alpha1Polygon from "./polygon.pb" +import * as CoinbaseStakingV1alpha1Solana from "./solana.pb" + +type Absent = { [k in Exclude]?: undefined }; +type OneOf = + | { [k in keyof T]?: undefined } + | ( + keyof T extends infer K ? + (K extends string & keyof T ? { [k in K]: T[K] } & Absent + : never) + : never); + +export enum TxStepOutputState { + STATE_UNSPECIFIED = "STATE_UNSPECIFIED", + STATE_NOT_CONSTRUCTED = "STATE_NOT_CONSTRUCTED", + STATE_CONSTRUCTED = "STATE_CONSTRUCTED", + STATE_PENDING_SIGNING = "STATE_PENDING_SIGNING", + STATE_SIGNED = "STATE_SIGNED", + STATE_BROADCASTING = "STATE_BROADCASTING", + STATE_CONFIRMING = "STATE_CONFIRMING", + STATE_CONFIRMED = "STATE_CONFIRMED", + STATE_FINALIZED = "STATE_FINALIZED", + STATE_FAILED = "STATE_FAILED", + STATE_SUCCESS = "STATE_SUCCESS", + STATE_CANCELING = "STATE_CANCELING", + STATE_CANCELED = "STATE_CANCELED", + STATE_CANCEL_FAILED = "STATE_CANCEL_FAILED", + STATE_FAILED_REFRESHABLE = "STATE_FAILED_REFRESHABLE", + STATE_REFRESHING = "STATE_REFRESHING", + STATE_PENDING_EXT_BROADCAST = "STATE_PENDING_EXT_BROADCAST", +} + +export enum WaitStepOutputWaitUnit { + WAIT_UNIT_UNSPECIFIED = "WAIT_UNIT_UNSPECIFIED", + WAIT_UNIT_SECONDS = "WAIT_UNIT_SECONDS", + WAIT_UNIT_BLOCKS = "WAIT_UNIT_BLOCKS", + WAIT_UNIT_EPOCHS = "WAIT_UNIT_EPOCHS", + WAIT_UNIT_CHECKPOINTS = "WAIT_UNIT_CHECKPOINTS", +} + +export enum WaitStepOutputState { + STATE_UNSPECIFIED = "STATE_UNSPECIFIED", + STATE_NOT_STARTED = "STATE_NOT_STARTED", + STATE_IN_PROGRESS = "STATE_IN_PROGRESS", + STATE_COMPLETED = "STATE_COMPLETED", +} + +export enum WorkflowState { + STATE_UNSPECIFIED = "STATE_UNSPECIFIED", + STATE_IN_PROGRESS = "STATE_IN_PROGRESS", + STATE_WAITING_FOR_SIGNING = "STATE_WAITING_FOR_SIGNING", + STATE_COMPLETED = "STATE_COMPLETED", + STATE_FAILED = "STATE_FAILED", + STATE_CANCELING = "STATE_CANCELING", + STATE_CANCELED = "STATE_CANCELED", + STATE_CANCEL_FAILED = "STATE_CANCEL_FAILED", + STATE_FAILED_REFRESHABLE = "STATE_FAILED_REFRESHABLE", + STATE_WAITING_FOR_EXT_BROADCAST = "STATE_WAITING_FOR_EXT_BROADCAST", +} + +export type TxStepOutput = { + unsignedTx?: string + signedTx?: string + txHash?: string + state?: TxStepOutputState + errorMessage?: string +} + +export type WaitStepOutput = { + start?: string + current?: string + target?: string + unit?: WaitStepOutputWaitUnit + state?: WaitStepOutputState +} + + +type BaseWorkflowStep = { + name?: string +} + +export type WorkflowStep = BaseWorkflowStep + & OneOf<{ txStepOutput: TxStepOutput; waitStepOutput: WaitStepOutput }> + + +type BaseWorkflow = { + name?: string + action?: string + state?: WorkflowState + currentStepId?: number + steps?: WorkflowStep[] + createTime?: GoogleProtobufTimestamp.Timestamp + updateTime?: GoogleProtobufTimestamp.Timestamp + skipBroadcast?: boolean + completeTime?: GoogleProtobufTimestamp.Timestamp +} + +export type Workflow = BaseWorkflow + & OneOf<{ polygonStakingParameters: CoinbaseStakingV1alpha1Polygon.PolygonStakingParameters; solanaStakingParameters: CoinbaseStakingV1alpha1Solana.SolanaStakingParameters; ethereumKilnStakingParameters: CoinbaseStakingV1alpha1Ethereum_kiln.EthereumKilnStakingParameters }> + +export type CreateWorkflowRequest = { + parent?: string + workflow?: Workflow +} + +export type GetWorkflowRequest = { + name?: string +} + +export type ListWorkflowsRequest = { + parent?: string + filter?: string + pageSize?: number + pageToken?: string +} + +export type ListWorkflowsResponse = { + workflows?: Workflow[] + nextPageToken?: string +} + +export type PerformWorkflowStepRequest = { + name?: string + step?: number + data?: string +} + +export type RefreshWorkflowStepRequest = { + name?: string + step?: number +} \ No newline at end of file diff --git a/src/gen/fetch.pb.ts b/src/gen/fetch.pb.ts new file mode 100644 index 0000000..8273636 --- /dev/null +++ b/src/gen/fetch.pb.ts @@ -0,0 +1,341 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +/** + * base64 encoder and decoder + * Copied and adapted from https://github.com/protobufjs/protobuf.js/blob/master/lib/base64/index.js + */ +// Base64 encoding table +const b64 = new Array(64); + +// Base64 decoding table +const s64 = new Array(123); + +// 65..90, 97..122, 48..57, 43, 47 +for (let i = 0; i < 64;) + s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++; + +export function b64Encode(buffer: Uint8Array, start: number, end: number): string { + let parts: string[] = null; + const chunk = []; + let i = 0, // output index + j = 0, // goto index + t; // temporary + while (start < end) { + const b = buffer[start++]; + switch (j) { + case 0: + chunk[i++] = b64[b >> 2]; + t = (b & 3) << 4; + j = 1; + break; + case 1: + chunk[i++] = b64[t | b >> 4]; + t = (b & 15) << 2; + j = 2; + break; + case 2: + chunk[i++] = b64[t | b >> 6]; + chunk[i++] = b64[b & 63]; + j = 0; + break; + } + if (i > 8191) { + (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); + i = 0; + } + } + if (j) { + chunk[i++] = b64[t]; + chunk[i++] = 61; + if (j === 1) + chunk[i++] = 61; + } + if (parts) { + if (i) + parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); + return parts.join(""); + } + return String.fromCharCode.apply(String, chunk.slice(0, i)); +} + +const invalidEncoding = "invalid encoding"; + +export function b64Decode(s: string): Uint8Array { + const buffer = []; + let offset = 0; + let j = 0, // goto index + t; // temporary + for (let i = 0; i < s.length;) { + let c = s.charCodeAt(i++); + if (c === 61 && j > 1) + break; + if ((c = s64[c]) === undefined) + throw Error(invalidEncoding); + switch (j) { + case 0: + t = c; + j = 1; + break; + case 1: + buffer[offset++] = t << 2 | (c & 48) >> 4; + t = c; + j = 2; + break; + case 2: + buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2; + t = c; + j = 3; + break; + case 3: + buffer[offset++] = (t & 3) << 6 | c; + j = 0; + break; + } + } + if (j === 1) + throw Error(invalidEncoding); + return new Uint8Array(buffer); +} + +function b64Test(s: string): boolean { + return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(s); +} + +export interface InitReq extends RequestInit { + pathPrefix?: string +} + +export function replacer(key: any, value: any): any { + if(value && value.constructor === Uint8Array) { + return b64Encode(value, 0, value.length); + } + + return value; +} + +export function fetchReq(path: string, init?: InitReq): Promise { + const {pathPrefix, ...req} = init || {} + + const url = pathPrefix ? `${pathPrefix}${path}` : path + + return fetch(url, req).then(r => r.json().then((body: O) => { + if (!r.ok) { throw body; } + return body; + })) as Promise +} + +// NotifyStreamEntityArrival is a callback that will be called on streaming entity arrival +export type NotifyStreamEntityArrival = (resp: T) => void + +/** + * fetchStreamingRequest is able to handle grpc-gateway server side streaming call + * it takes NotifyStreamEntityArrival that lets users respond to entity arrival during the call + * all entities will be returned as an array after the call finishes. + **/ +export async function fetchStreamingRequest(path: string, callback?: NotifyStreamEntityArrival, init?: InitReq) { + const {pathPrefix, ...req} = init || {} + const url = pathPrefix ?`${pathPrefix}${path}` : path + const result = await fetch(url, req) + // needs to use the .ok to check the status of HTTP status code + // http other than 200 will not throw an error, instead the .ok will become false. + // see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch# + if (!result.ok) { + const resp = await result.json() + const errMsg = resp.error && resp.error.message ? resp.error.message : "" + throw new Error(errMsg) + } + + if (!result.body) { + throw new Error("response doesnt have a body") + } + + await result.body + .pipeThrough(new TextDecoderStream()) + .pipeThrough(getNewLineDelimitedJSONDecodingStream()) + .pipeTo(getNotifyEntityArrivalSink((e: R) => { + if (callback) { + callback(e) + } + })) + + // wait for the streaming to finish and return the success respond + return +} + +/** + * JSONStringStreamController represents the transform controller that's able to transform the incoming + * new line delimited json content stream into entities and able to push the entity to the down stream + */ +interface JSONStringStreamController extends TransformStreamDefaultController { + buf?: string + pos?: number + enqueue: (s: T) => void +} + +/** + * getNewLineDelimitedJSONDecodingStream returns a TransformStream that's able to handle new line delimited json stream content into parsed entities + */ +function getNewLineDelimitedJSONDecodingStream(): TransformStream { + return new TransformStream({ + start(controller: JSONStringStreamController) { + controller.buf = '' + controller.pos = 0 + }, + + transform(chunk: string, controller: JSONStringStreamController) { + if (controller.buf === undefined) { + controller.buf = '' + } + if (controller.pos === undefined) { + controller.pos = 0 + } + controller.buf += chunk + while (controller.pos < controller.buf.length) { + if (controller.buf[controller.pos] === '\n') { + const line = controller.buf.substring(0, controller.pos) + const response = JSON.parse(line) + controller.enqueue(response.result) + controller.buf = controller.buf.substring(controller.pos + 1) + controller.pos = 0 + } else { + ++controller.pos + } + } + } + }) + +} + +/** + * getNotifyEntityArrivalSink takes the NotifyStreamEntityArrival callback and return + * a sink that will call the callback on entity arrival + * @param notifyCallback + */ +function getNotifyEntityArrivalSink(notifyCallback: NotifyStreamEntityArrival) { + return new WritableStream({ + write(entity: T) { + notifyCallback(entity) + } + }) +} + +type Primitive = string | boolean | number; +type RequestPayload = Record; +type FlattenedRequestPayload = Record>; + +/** + * Checks if given value is a plain object + * Logic copied and adapted from below source: + * https://github.com/char0n/ramda-adjunct/blob/master/src/isPlainObj.js + * @param {unknown} value + * @return {boolean} + */ +function isPlainObject(value: unknown): boolean { + const isObject = + Object.prototype.toString.call(value).slice(8, -1) === "Object"; + const isObjLike = value !== null && isObject; + + if (!isObjLike || !isObject) { + return false; + } + + const proto = Object.getPrototypeOf(value); + + const hasObjectConstructor = + typeof proto === "object" && + proto.constructor === Object.prototype.constructor; + + return hasObjectConstructor; +} + +/** + * Checks if given value is of a primitive type + * @param {unknown} value + * @return {boolean} + */ +function isPrimitive(value: unknown): boolean { + return ["string", "number", "boolean"].some(t => typeof value === t); +} + +/** + * Checks if given primitive is zero-value + * @param {Primitive} value + * @return {boolean} + */ +function isZeroValuePrimitive(value: Primitive): boolean { + return value === false || value === 0 || value === ""; +} + +/** + * Flattens a deeply nested request payload and returns an object + * with only primitive values and non-empty array of primitive values + * as per https://github.com/googleapis/googleapis/blob/master/google/api/http.proto + * @param {RequestPayload} requestPayload + * @param {String} path + * @return {FlattenedRequestPayload>} + */ +function flattenRequestPayload( + requestPayload: T, + path: string = "" +): FlattenedRequestPayload { + return Object.keys(requestPayload).reduce( + (acc: T, key: string): T => { + const value = requestPayload[key]; + const newPath = path ? [path, key].join(".") : key; + + const isNonEmptyPrimitiveArray = + Array.isArray(value) && + value.every(v => isPrimitive(v)) && + value.length > 0; + + const isNonZeroValuePrimitive = + isPrimitive(value) && !isZeroValuePrimitive(value as Primitive); + + let objectToMerge = {}; + + if (isPlainObject(value)) { + objectToMerge = flattenRequestPayload(value as RequestPayload, newPath); + } else if (isNonZeroValuePrimitive || isNonEmptyPrimitiveArray) { + objectToMerge = { [newPath]: value }; + } + + return { ...acc, ...objectToMerge }; + }, + {} as T + ) as FlattenedRequestPayload; +} + +/** + * Renders a deeply nested request payload into a string of URL search + * parameters by first flattening the request payload and then removing keys + * which are already present in the URL path. + * @param {RequestPayload} requestPayload + * @param {string[]} urlPathParams + * @return {string} + */ +export function renderURLSearchParams( + requestPayload: T, + urlPathParams: string[] = [] +): string { + const flattenedRequestPayload = flattenRequestPayload(requestPayload); + + const urlSearchParams = Object.keys(flattenedRequestPayload).reduce( + (acc: string[][], key: string): string[][] => { + // key should not be present in the url path as a parameter + const value = flattenedRequestPayload[key]; + if (urlPathParams.find(f => f === key)) { + return acc; + } + return Array.isArray(value) + ? [...acc, ...value.map(m => [key, m.toString()])] + : (acc = [...acc, [key, value.toString()]]); + }, + [] as string[][] + ); + + return new URLSearchParams(urlSearchParams).toString(); +} \ No newline at end of file diff --git a/src/gen/google/protobuf/timestamp.pb.ts b/src/gen/google/protobuf/timestamp.pb.ts new file mode 100644 index 0000000..283bbd1 --- /dev/null +++ b/src/gen/google/protobuf/timestamp.pb.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type Timestamp = { + seconds?: string + nanos?: number +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..091e243 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +import * as Utils from "./utils/date"; + +export { Utils }; + +export { StakingServiceClient } from "./client/staking-service-client"; diff --git a/src/signers/ethereum_signer.ts b/src/signers/ethereum_signer.ts new file mode 100644 index 0000000..626bbca --- /dev/null +++ b/src/signers/ethereum_signer.ts @@ -0,0 +1,26 @@ +import { TransactionFactory } from "@ethereumjs/tx"; +import { bytesToHex } from "@ethereumjs/util"; +import { TxSigner } from "./txsigner"; + +export class EthereumTransactionSigner implements TxSigner { + async signTransaction( + privateKey: string, + unsignedTx: string, + ): Promise { + let transaction = TransactionFactory.fromSerializedData( + Buffer.from(unsignedTx, "hex"), + ); + + const decodedPrivateKey = Buffer.from(privateKey, "hex"); + + let signedTx = transaction.sign(decodedPrivateKey); + + const verifiedSignature = signedTx.verifySignature(); + + if (!verifiedSignature) { + throw new Error("Produced an invalid signature!"); + } + + return bytesToHex(signedTx.serialize()); + } +} diff --git a/src/signers/index.ts b/src/signers/index.ts new file mode 100644 index 0000000..8dc7b48 --- /dev/null +++ b/src/signers/index.ts @@ -0,0 +1,2 @@ +export * from "./ethereum_signer"; +export * from "./txsigner"; diff --git a/src/signers/txsigner.ts b/src/signers/txsigner.ts new file mode 100644 index 0000000..16f4200 --- /dev/null +++ b/src/signers/txsigner.ts @@ -0,0 +1,18 @@ +import { EthereumTransactionSigner } from "./ethereum_signer"; + +export interface TxSigner { + // eslint-disable-next-line no-unused-vars + signTransaction(privateKey: string, unsignedTx: string): Promise; +} + +export class TxSignerFactory { + static getSigner(protocol: string): TxSigner { + switch (protocol) { + case "ethereum": + return new EthereumTransactionSigner(); + // other cases for additional protocols + default: + throw new Error("Unsupported protocol"); + } + } +} diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 0000000..1acec6c --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,10 @@ +// calculateTimeDifference is a function that takes two timestamps and returns the difference in seconds. +export function calculateTimeDifference( + timestamp1: string, + timestamp2: string, +): number { + const date1 = new Date(timestamp1); + const date2 = new Date(timestamp2); + + return Math.abs(date1.getTime() - date2.getTime()) / 1000; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f49258b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "module": "CommonJS", /* Specify what module code is generated. */ + "outDir": "./dist", + "rootDir": "./src", + "sourceMap": true, + "declaration": true, + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "strict": true, /* Enable all strict type-checking options. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "openapi"] +}