diff --git a/.eslintrc.json b/.eslintrc.json index dc356b7..938d5f0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,9 +4,13 @@ "node": true }, "extends": [ + "prettier", "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", - "prettier" + "plugin:@typescript-eslint/recommended", + // NOTE: This needs to be last, per documentation: + // https://github.com/prettier/eslint-plugin-prettier?tab=readme-ov-file#configuration-legacy-eslintrc + "plugin:prettier/recommended" ], "globals": { "Atomics": "readonly", @@ -19,15 +23,68 @@ }, "plugins": [ "@typescript-eslint", + "filename-rules", "prettier" ], "rules": { + "filename-rules/match": [ + 2, + "kebab-case" + ], "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" + "prettier/prettier": [ + "error", + { + "singleQuote": true + }, + { + "trailingComma": "none" + } + ], + "prefer-const": "off", + // NOTE: We can't enable TS compilation to enforce function return types + // Source: https://github.com/Microsoft/TypeScript/issues/18529 + // So instead we enforce it with linting + "@typescript-eslint/explicit-function-return-type": "error", + "object-curly-spacing": [ + "error", + "always" + ], + "padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": [ + "const", + "let", + "var" + ], + "next": "*" + }, + { + "blankLine": "any", + "prev": [ + "const", + "let", + "var" + ], + "next": [ + "const", + "let", + "var" + ] + } + ], + "lines-between-class-members": [ + "error", + "always", + { + "exceptAfterSingleLine": true + } + ] } -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index 7f96b19..737fe95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # IDE/Tools .docker .idea -/.vscode # vim .*.sw? diff --git a/LICENSE b/LICENSE index 76ec30d..ef3e6bf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,201 @@ -Copyright (c) 2018-2023 Coinbase, Inc. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -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 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - http://www.apache.org/licenses/LICENSE-2.0 + 1. Definitions. -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 + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2018-2024 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. diff --git a/README.md b/README.md index 96e31ff..250c122 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,159 @@ -# Staking API Typescript Client Library +Coinbase Staking API -[![npm version](https://badge.fury.io/js/@coinbase%2Fstaking-client-library-ts.svg)](https://badge.fury.io/js/@coinbase%2Fstaking-client-library-ts) +# [Coinbase Staking API](https://github.com/coinbase/staking-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. +> Programmatic access to Coinbase's best-in-class staking infrastructure and services. :large_blue_circle: + +[![npm version](https://badge.fury.io/js/@coinbase%2Fstaking-client-library-ts.svg)](https://badge.fury.io/js/@coinbase%2Fstaking-client-library-ts) [![Current version](https://img.shields.io/github/tag/coinbase/staking-client-library-ts?color=3498DB&label=version)](https://github.com/coinbase/staking-client-library-ts/releases) [![GitHub contributors](https://img.shields.io/github/contributors/coinbase/staking-client-library-ts?color=3498DB)](https://github.com/coinbase/staking-client-library-ts/graphs/contributors) [![GitHub Stars](https://img.shields.io/github/stars/coinbase/staking-client-library-ts.svg?color=3498DB)](https://github.com/coinbase/staking-client-library-ts/stargazers) [![GitHub](https://img.shields.io/github/license/coinbase/staking-client-library-ts?color=3498DB)](https://github.com/coinbase/staking-client-library-ts/blob/main/LICENSE) ## Overview -Staking API provides a set of APIs to aid in non-custodial staking for multiple protocols and networks. +`staking-client-library-ts` is the Typescript SDK for the **Coinbase Staking API** :large_blue_circle:. + +The Coinbase Staking API empowers developers to deliver a fully-featured staking experience in their Web2 apps, wallets, or dApps using *one common interface* across protocols. + +A traditional infrastructure-heavy staking integration can take months. Coinbase's Staking API enables onboarding within hours :sparkles:. + +## Quick Start + +Prerequisite: [Node 18.12+](https://nodejs.org/en/blog/release/v18.12.0) + +1. Install this package: `npm install @coinbase/staking-client-library-ts` +2. Create and download an API key from the [Cloud Platform](https://portal.cloud.coinbase.com/access/api). +3. Place the key named `.coinbase_cloud_api_key.json` at the root of this repository. +4. Run one of the code samples [below](#stake-partial-eth-💠) or any of our [provided examples](./examples/) :rocket:. + +### Stake Partial ETH :diamond_shape_with_a_dot_inside: + +This code sample creates an ETH staking workflow. View the full code sample [here](examples/ethereum/create-workflow.ts) -## Prerequisites +
+ Code Sample -- [Node 18.12.0 or higher](https://nodejs.org/en/blog/release/v18.12.0) +```typescript +// examples/ethereum/create-workflow.ts +import { StakingClient } from "@coinbase/staking-client-library-ts"; -## 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. +const client = new StakingClient(); -## Get Started -To test that your API Key gives you access as expected to the Staking APIs: +client.Ethereum.stake( + 'your-project-id', // replace with your project id + 'holesky', + true, + 'your-wallet-address', // replace with your wallet address + '0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b', + '123', +) + .then((workflow) => { + console.log('Workflow created %s', workflow.name); + }) + .catch(() => { + throw new Error(`Error creating workflow`); + }); +``` + +
+ +
+ Output -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 + Workflow created: projects/62376b2f-3f24-42c9-9025-d576a3c06d6f/workflows/ffbf9b45-c57b-49cb-a4d5-fdab66d8cb25 + ``` + +
+ +### View Ethereum Rewards :moneybag: + +This code sample returns rewards for an Ethereum validator address. View the full code sample [here](examples/ethereum/list-rewards.ts). + +
+ Code Sample + +```typescript +import { StakingClient } from "@coinbase/staking-client-library-ts"; + +// Defines which address and rewards we want to see +const address: string = + '0xac53512c39d0081ca4437c285305eb423f474e6153693c12fbba4a3df78bcaa3422b31d800c5bea71c1b017168a60474'; +const filter: string = `address='${address}' AND period_end_time > '2024-02-25T00:00:00Z' AND period_end_time < '2024-02-27T00:00:00Z'`; + +const client = new StakingClient(); + +// Loops through rewards array and prints each reward +client.Ethereum.listRewards(filter).then((resp) => { + resp.rewards!.forEach((reward) => { + console.log(JSON.stringify(reward, null, 2)); + }); +}); +``` + +
+ +
+ Output + + ```json { - 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: [ + "address": "0xac53512c39d0081ca4437c285305eb423f474e6153693c12fbba4a3df78bcaa3422b31d800c5bea71c1b017168a60474", + "date": "2024-02-25", + "aggregationUnit": "DAY", + "periodStartTime": "2024-02-25T00:00:00Z", + "periodEndTime": "2024-02-25T23:59:59Z", + "totalEarnedNativeUnit": { + "amount": "0.002183619", + "exp": "18", + "ticker": "ETH", + "rawNumeric": "2183619000000000" + }, + "totalEarnedUsd": [ { - name: 'protocols/ethereum_kiln' + "source": "COINBASE_EXCHANGE", + "conversionTime": "2024-02-26T00:09:00Z", + "amount": { + "amount": "6.79", + "exp": "2", + "ticker": "USD", + "rawNumeric": "679" + }, + "conversionPrice": "3105.780029" } - ] + ], + "endingBalance": null, + "protocol": "ethereum" } { - networks: [ + "address": "0xac53512c39d0081ca4437c285305eb423f474e6153693c12fbba4a3df78bcaa3422b31d800c5bea71c1b017168a60474", + "date": "2024-02-26", + "aggregationUnit": "DAY", + "periodStartTime": "2024-02-26T00:00:00Z", + "periodEndTime": "2024-02-26T23:59:59Z", + "totalEarnedNativeUnit": { + "amount": "0.002182946", + "exp": "18", + "ticker": "ETH", + "rawNumeric": "2182946000000000" + }, + "totalEarnedUsd": [ { - name: 'protocols/ethereum_kiln/networks/goerli', - isMainnet: false - }, - { - name: 'protocols/ethereum_kiln/networks/mainnet', - isMainnet: true + "source": "COINBASE_EXCHANGE", + "conversionTime": "2024-02-27T00:09:00Z", + "amount": { + "amount": "6.94", + "exp": "2", + "ticker": "USD", + "rawNumeric": "694" + }, + "conversionPrice": "3178.889893" } - ] + ], + "endingBalance": null, + "protocol": "ethereum" } ``` -## 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(); - ``` +
+ +## Documentation + +There are numerous examples in the [`examples directory`](./examples) to help get you started. For even more, refer to our [documentation website](https://docs.cloud.coinbase.com/) for detailed definitions, API specifications, integration guides, and more! diff --git a/docs/images/banner.svg b/docs/images/banner.svg new file mode 100644 index 0000000..af98a42 --- /dev/null +++ b/docs/images/banner.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/openapi/orchestration.swagger.json b/docs/openapi/orchestration.swagger.json index 1ad5810..39d3a86 100644 --- a/docs/openapi/orchestration.swagger.json +++ b/docs/openapi/orchestration.swagger.json @@ -1,9 +1,9 @@ { "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" + "title": "Orchestration Service", + "description": "Service that can power non-custodial staking experiences for your users.", + "version": "v1" }, "tags": [ { @@ -14,14 +14,18 @@ "name": "Network", "description": "Networks details" }, - { - "name": "Validator", - "description": "Validators details" - }, { "name": "Action", "description": "Actions details" }, + { + "name": "StakingTarget", + "description": "Staking targets details" + }, + { + "name": "StakingContext", + "description": "Staking context details" + }, { "name": "Workflow", "description": "Workflow management details" @@ -42,7 +46,7 @@ "application/json" ], "paths": { - "/api/v1alpha1/protocols": { + "/v1/protocols": { "get": { "summary": "List supported protocols", "description": "List supported protocols", @@ -51,7 +55,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1ListProtocolsResponse" + "$ref": "#/definitions/v1ListProtocolsResponse" } }, "400": { @@ -102,7 +106,7 @@ ] } }, - "/api/v1alpha1/viewStakingContext:view": { + "/v1/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", @@ -111,7 +115,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1ViewStakingContextResponse" + "$ref": "#/definitions/v1ViewStakingContextResponse" } }, "400": { @@ -185,7 +189,7 @@ ] } }, - "/api/v1alpha1/{name}": { + "/v1/{name}": { "get": { "summary": "Get workflow", "operationId": "getWorkflow", @@ -193,77 +197,7 @@ "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" + "$ref": "#/definitions/v1Workflow" } }, "400": { @@ -317,25 +251,6 @@ "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": [ @@ -343,7 +258,7 @@ ] } }, - "/api/v1alpha1/{name}/step": { + "/v1/{name}/step": { "post": { "summary": "Perform the next step in a workflow", "description": "Perform the next step in a workflow", @@ -352,7 +267,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1Workflow" + "$ref": "#/definitions/v1Workflow" } }, "400": { @@ -412,23 +327,7 @@ "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" - ] + "$ref": "#/definitions/StakingServicePerformWorkflowStepBody" } } ], @@ -437,7 +336,7 @@ ] } }, - "/api/v1alpha1/{parent}/actions": { + "/v1/{parent}/actions": { "get": { "summary": "List supported actions", "description": "List supported actions", @@ -446,7 +345,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1ListActionsResponse" + "$ref": "#/definitions/v1ListActionsResponse" } }, "400": { @@ -507,7 +406,7 @@ ] } }, - "/api/v1alpha1/{parent}/networks": { + "/v1/{parent}/networks": { "get": { "summary": "List supported networks", "description": "List supported networks", @@ -516,7 +415,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1ListNetworksResponse" + "$ref": "#/definitions/v1ListNetworksResponse" } }, "400": { @@ -577,7 +476,7 @@ ] } }, - "/api/v1alpha1/{parent}/stakingTargets": { + "/v1/{parent}/stakingTargets": { "get": { "summary": "List supported staking targets", "description": "List supported staking targets", @@ -586,7 +485,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1ListStakingTargetsResponse" + "$ref": "#/definitions/v1ListStakingTargetsResponse" } }, "400": { @@ -662,7 +561,7 @@ ] } }, - "/api/v1alpha1/{parent}/workflows": { + "/v1/{parent}/workflows": { "get": { "summary": "List supported workflows", "operationId": "listWorkflows", @@ -670,7 +569,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1ListWorkflowsResponse" + "$ref": "#/definitions/v1ListWorkflowsResponse" } }, "400": { @@ -727,7 +626,7 @@ }, { "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\"", + "description": "[AIP-160](https://google.aip.dev/160) filter\nSupported fields:\n- string action: \"stake\", \"unstake\"\n- string protocol: \"ethereum_kiln\"\n- string network: \"holesky\", \"mainnet\"", "in": "query", "required": false, "type": "string" @@ -759,7 +658,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1alpha1Workflow" + "$ref": "#/definitions/v1Workflow" } }, "400": { @@ -820,7 +719,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1alpha1Workflow", + "$ref": "#/definitions/v1Workflow", "required": [ "workflow" ] @@ -834,6 +733,37 @@ } }, "definitions": { + "StakeAccountBalanceState": { + "type": "string", + "enum": [ + "BALANCE_STATE_UNSPECIFIED", + "BALANCE_STATE_INACTIVE", + "BALANCE_STATE_ACTIVATING", + "BALANCE_STATE_ACTIVE", + "BALANCE_STATE_DEACTIVATING" + ], + "default": "BALANCE_STATE_UNSPECIFIED", + "description": "Represents the different states a stake account balance can have.\nUsed to check to see if stake is actively earning rewards or ready to be withdrawn.\n\n - BALANCE_STATE_UNSPECIFIED: The balance is not known.\n - BALANCE_STATE_INACTIVE: The balance is not actively staking.\n - BALANCE_STATE_ACTIVATING: The balance is in a warm up period and will activate in the next epoch.\n - BALANCE_STATE_ACTIVE: The balance is actively staking and earning rewards.\n - BALANCE_STATE_DEACTIVATING: The balance is in a cool down period and will be deactivated in the next epoch." + }, + "StakingServicePerformWorkflowStepBody": { + "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" + ] + }, "WaitStepOutputWaitUnit": { "type": "string", "enum": [ @@ -874,17 +804,17 @@ } } }, - "v1alpha1Action": { + "v1Action": { "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" + "title": "The resource name of the Action.\nFormat: protocols/{protocolName}/networks/{networkName}/actions/{actionName}\nEx: protocols/ethereum_kiln/networks/holesky/validators/stake" } }, - "description": "An Action resource, which represents an action you may take on a network,\nposted to a validator (e.g. stake, unstake)." + "description": "An Action resource represents an action you may take on a network (e.g. stake, unstake)." }, - "v1alpha1Amount": { + "v1Amount": { "type": "object", "properties": { "value": { @@ -898,12 +828,12 @@ }, "description": "The amount of a token you wish to perform an action\nwith." }, - "v1alpha1Contract": { + "v1Contract": { "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" + "title": "The resource name of the Contract Address.\nFormat: protocols/{protocolName}/networks/{networkName}/stakingTargets/{contractName}\nEx: protocols/ethereum_kiln/networks/holesky/stakingTargets/0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b" }, "address": { "type": "string", @@ -912,7 +842,7 @@ }, "description": "A Contract resource, which represents an active contract\nfor the given protocol network which you can submit an action\nto." }, - "v1alpha1EthereumKilnClaimStakeParameters": { + "v1EthereumKilnClaimStakeParameters": { "type": "object", "properties": { "stakerAddress": { @@ -931,7 +861,7 @@ "integratorContractAddress" ] }, - "v1alpha1EthereumKilnStakeParameters": { + "v1EthereumKilnStakeParameters": { "type": "object", "properties": { "stakerAddress": { @@ -943,7 +873,7 @@ "description": "The address of the integrator contract." }, "amount": { - "$ref": "#/definitions/v1alpha1Amount", + "$ref": "#/definitions/v1Amount", "description": "The amount of Ethereum to stake in wei." } }, @@ -955,38 +885,38 @@ "amount" ] }, - "v1alpha1EthereumKilnStakingContextDetails": { + "v1EthereumKilnStakingContextDetails": { "type": "object", "properties": { "ethereumBalance": { - "$ref": "#/definitions/v1alpha1Amount", + "$ref": "#/definitions/v1Amount", "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", + "$ref": "#/definitions/v1Amount", "description": "The number of integrator shares owned by the address." }, "integratorShareUnderlyingBalance": { - "$ref": "#/definitions/v1alpha1Amount", + "$ref": "#/definitions/v1Amount", "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", + "$ref": "#/definitions/v1Amount", "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", + "$ref": "#/definitions/v1Amount", "description": "The number of vPool shares are eligible to receive now or at a later point in time." }, "fulfillableShareCount": { - "$ref": "#/definitions/v1alpha1Amount", + "$ref": "#/definitions/v1Amount", "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": { + "v1EthereumKilnStakingContextParameters": { "type": "object", "properties": { "integratorContractAddress": { @@ -997,26 +927,26 @@ "description": "The protocol specific parameters required for fetching a staking context.", "title": "EthereumKiln: Staking Context Parameters" }, - "v1alpha1EthereumKilnStakingParameters": { + "v1EthereumKilnStakingParameters": { "type": "object", "properties": { "stakeParameters": { - "$ref": "#/definitions/v1alpha1EthereumKilnStakeParameters", + "$ref": "#/definitions/v1EthereumKilnStakeParameters", "description": "The parameters for stake action on Ethereum Kiln." }, "unstakeParameters": { - "$ref": "#/definitions/v1alpha1EthereumKilnUnstakeParameters", + "$ref": "#/definitions/v1EthereumKilnUnstakeParameters", "description": "The parameters for unstake action on Ethereum Kiln." }, "claimStakeParameters": { - "$ref": "#/definitions/v1alpha1EthereumKilnClaimStakeParameters", + "$ref": "#/definitions/v1EthereumKilnClaimStakeParameters", "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": { + "v1EthereumKilnUnstakeParameters": { "type": "object", "properties": { "stakerAddress": { @@ -1028,7 +958,7 @@ "description": "The address of the integrator contract." }, "amount": { - "$ref": "#/definitions/v1alpha1Amount", + "$ref": "#/definitions/v1Amount", "description": "The amount of Ethereum to unstake in wei." } }, @@ -1040,56 +970,56 @@ "amount" ] }, - "v1alpha1ListActionsResponse": { + "v1ListActionsResponse": { "type": "object", "properties": { "actions": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1alpha1Action" + "$ref": "#/definitions/v1Action" }, "description": "The list of actions." } }, "description": "The response message for ListActions." }, - "v1alpha1ListNetworksResponse": { + "v1ListNetworksResponse": { "type": "object", "properties": { "networks": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1alpha1Network" + "$ref": "#/definitions/v1Network" }, "description": "The list of networks." } }, "description": "The response message for ListNetworks." }, - "v1alpha1ListProtocolsResponse": { + "v1ListProtocolsResponse": { "type": "object", "properties": { "protocols": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1alpha1Protocol" + "$ref": "#/definitions/v1Protocol" }, "description": "The list of protocols." } }, "description": "The response message for ListProtocols." }, - "v1alpha1ListStakingTargetsResponse": { + "v1ListStakingTargetsResponse": { "type": "object", "properties": { "stakingTargets": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1alpha1StakingTarget" + "$ref": "#/definitions/v1StakingTarget" }, "description": "The list of staking targets." }, @@ -1100,14 +1030,14 @@ }, "description": "The response message for ListStakingTargets." }, - "v1alpha1ListWorkflowsResponse": { + "v1ListWorkflowsResponse": { "type": "object", "properties": { "workflows": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1alpha1Workflow" + "$ref": "#/definitions/v1Workflow" }, "description": "The list of workflows." }, @@ -1118,384 +1048,205 @@ }, "description": "The response message for ListWorkflows." }, - "v1alpha1Network": { + "v1Network": { "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." + "title": "The resource name of the Network.\nFormat: protocols/{protocolName}/networks/{networkName}\nEx: protocols/ethereum_kiln/networks/holesky" } }, - "description": "The parameters required to use a durable transaction nonce for Solana transactions.", - "title": "Solana: Nonce Options" + "description": "A Network resource represents a blockchain network e.g. mainnet, testnet, etc." }, - "v1alpha1PolygonClaimRewardsParameters": { + "v1PriorityFee": { "type": "object", "properties": { - "delegatorAddress": { + "computeUnitLimit": { "type": "string", - "description": "The public address of the delegator you wish to\ninteract with." + "format": "int64", + "description": "The maximum number of compute units a transaction is allowed to consume." }, - "validatorAddress": { + "unitPrice": { "type": "string", - "description": "The public address of the validator you wish to\nperform the action to." + "format": "int64", + "description": "The price to pay per compute unit." } }, - "description": "The parameters required for claim rewards action on Polygon.", - "title": "The parameters needed to claim rewards on Polygon.", - "required": [ - "delegatorAddress", - "validatorAddress" - ] + "description": "A prioritization fee that can be added to a Solana transaction." }, - "v1alpha1PolygonRestakeParameters": { + "v1Protocol": { "type": "object", "properties": { - "delegatorAddress": { - "type": "string", - "description": "The public address of the delegator you wish to\ninteract with." - }, - "validatorAddress": { + "name": { "type": "string", - "description": "The public address of the validator you wish to\nperform the action to." + "title": "The resource name of the Protocol.\nFormat: protocols/{protocolName}\nEx: protocols/ethereum_kiln" } }, - "description": "The parameters required for unstake action on Polygon.", - "title": "The parameters needed to restake on Polygon.", - "required": [ - "delegatorAddress", - "validatorAddress" - ] + "description": "A Protocol resource (e.g. ethereum_kiln, solana etc.)." }, - "v1alpha1PolygonStakeParameters": { + "v1SolanaClaimStakeParameters": { "type": "object", "properties": { - "delegatorAddress": { + "walletAddress": { "type": "string", - "description": "The public address of the delegator you wish to\ninteract with." + "description": "The address which is the signing authority to claim stake." }, - "validatorAddress": { + "stakeAccountAddress": { "type": "string", - "description": "The public address of the validator you wish to\nperform the action to." + "description": "The address of the stake account to claim stake from." }, - "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." + "priorityFee": { + "$ref": "#/definitions/v1PriorityFee", + "description": "The option to set a priority fee for the transaction." } }, - "description": "The parameters required for stake action on Polygon.", - "title": "The parameters needed to stake on Polygon.", - "required": [ - "delegatorAddress", - "validatorAddress", - "amount" - ] + "description": "The parameters required to perform a claim stake operation on Solana.", + "title": "Solana: Claim Stake Parameters" }, - "v1alpha1PolygonStakingParameters": { + "v1SolanaStakeParameters": { "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": { + "walletAddress": { "type": "string", - "description": "The public address of the delegator you wish to\ninteract with." + "description": "The address where the funds are coming from to stake." }, "validatorAddress": { "type": "string", - "description": "The public address of the validator you wish to\nperform the action to." + "description": "The address of the validator." }, "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" + "$ref": "#/definitions/v1Amount", + "title": "The amount of Solana to stake in lamports. (1 lamport = 0.000000001 SOL)" + }, + "priorityFee": { + "$ref": "#/definitions/v1PriorityFee", + "description": "The option to set a priority fee for the transaction." } }, - "description": "A Protocol resource (e.g. polygon, ethereum, etc.)." + "description": "The parameters required to perform a stake operation on Solana.", + "title": "Solana: Stake Parameters" }, - "v1alpha1SolanaCreateStakeAccountParameters": { + "v1SolanaStakingContextDetails": { "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." + "balance": { + "$ref": "#/definitions/v1Amount", + "description": "The total balance of the main wallet address (system account).\nUsed to check the balance for any future staking or transaction to send." }, - "stakeAuthority": { + "currentEpoch": { "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." + "format": "int64", + "description": "The current epoch that the Solana blockchain is in.\nUsed as a frame of reference for future stake activations and deactivations." }, - "withdrawAuthority": { + "epochCompletionPercentage": { "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." + "description": "How much of the epoch has passed as a percentage.\nUsed to inform how much time is left before a stake is activated or deactivated." }, - "nonceOptions": { - "$ref": "#/definitions/v1alpha1NonceOptions", - "description": "The parameters required to use a durable transaction nonce for Solana transactions." + "stakeAccounts": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1StakeAccount" + }, + "description": "The list of staking accounts that are linked to the main wallet address (system account).\nUsed to check for statuses and balances of all stake accounts related to the main wallet address that\nthey're linked to." } }, - "description": "The parameters required for the create stake account action on Solana.", - "title": "Solana: Create Stake Account Parameters", - "required": [ - "stakeAccountAddress", - "fromAddress", - "amount" - ] + "description": "The protocol specific details for a Solana staking context.", + "title": "Solana: Staking Context Details" }, - "v1alpha1SolanaDeactivateStakeParameters": { + "v1SolanaStakingContextParameters": { "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" - ] + "description": "The protocol specific parameters required for fetching a staking context.", + "title": "Solana: Staking Context Parameters" }, - "v1alpha1SolanaDelegateStakeParameters": { + "v1SolanaStakingParameters": { "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." + "stakeParameters": { + "$ref": "#/definitions/v1SolanaStakeParameters", + "description": "The parameters for stake action on Solana." }, - "stakeAuthority": { - "type": "string", - "description": "The address of the account which has signing authority over staking operations on the stake account. Signs the transaction." + "unstakeParameters": { + "$ref": "#/definitions/v1SolanaUnstakeParameters", + "description": "The parameters for unstake action on Solana." }, - "nonceOptions": { - "$ref": "#/definitions/v1alpha1NonceOptions", - "description": "The parameters required to use a durable transaction nonce for Solana transactions." + "claimStakeParameters": { + "$ref": "#/definitions/v1SolanaClaimStakeParameters", + "description": "The parameters for claim stake action on Solana." } }, - "description": "The parameters required for the delegate stake action on Solana.", - "title": "Solana: Delegate Stake Parameters", - "required": [ - "stakeAccountAddress", - "voteAccountAddress", - "stakeAuthority" - ] + "description": "The parameters needed for staking on Solana.", + "title": "Solana: Staking Parameters" }, - "v1alpha1SolanaMergeStakeParameters": { + "v1SolanaUnstakeParameters": { "type": "object", "properties": { - "stakeAccountAddress": { + "walletAddress": { "type": "string", - "description": "The address of the stake account will be merged into and have stake added to it." + "description": "The address which is the signing authority to unstake." }, - "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." + "description": "The address of the stake account to unstake from." }, "amount": { - "$ref": "#/definitions/v1alpha1Amount", - "description": "The amount of stake to split, in Lamports." + "$ref": "#/definitions/v1Amount", + "title": "The amount of Solana to unstake in lamports. (1 lamport = 0.000000001 SOL)" }, - "nonceOptions": { - "$ref": "#/definitions/v1alpha1NonceOptions", - "description": "The parameters required to use a durable transaction nonce for Solana transactions." + "priorityFee": { + "$ref": "#/definitions/v1PriorityFee", + "description": "The option to set a priority fee for the transaction." } }, - "description": "The parameters required for split stake action on Solana.", - "title": "Solana: Split Stake Parameters", - "required": [ - "stakeAccountAddress", - "newStakeAccountAddress", - "stakeAuthority", - "amount" - ] + "description": "The parameters required to perform a unstake operation on Solana.", + "title": "Solana: Unstake Parameters" }, - "v1alpha1SolanaStakingParameters": { + "v1StakeAccount": { "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." + "address": { + "type": "string", + "description": "The address of the stake account.\nUsed to hold the staked funds transferred over from the main wallet." }, - "withdrawStakeParameters": { - "$ref": "#/definitions/v1alpha1SolanaWithdrawStakeParameters", - "description": "The parameters for withdraw stake action on Solana." + "bondedStake": { + "$ref": "#/definitions/v1Amount", + "description": "The bonded balance in lamports on the stake account (rent is not included in bonded amount).\nUsed to check the amount that is currently staked." }, - "splitStakeParameters": { - "$ref": "#/definitions/v1alpha1SolanaSplitStakeParameters", - "description": "The parameters for split stake action on Solana." + "rentReserve": { + "$ref": "#/definitions/v1Amount", + "description": "The rent amount for the stake account in lamports.\nUsed to highlight the amount used as the rent to maintain the address on the Solana blockchain." }, - "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." + "balance": { + "$ref": "#/definitions/v1Amount", + "description": "The total balance on the address in lamports.\nUsed to check the total balance for the stake account." }, - "recipientAddress": { - "type": "string", - "description": "The address of the recipient account which will receive the withdrawn stake." + "balanceState": { + "$ref": "#/definitions/StakeAccountBalanceState", + "description": "The balance state of the stake account.\nUsed to show what state the currently staked funds are in.", + "readOnly": true }, - "withdrawAuthority": { + "validator": { "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 validator (vote account) that the stake account is assigned to stake to.\nUsed to show where the staked funds are staked to." } }, - "description": "The parameters required for the withdraw stake action on Solana.", - "title": "Solana: Withdraw Stake Parameters", - "required": [ - "stakeAccountAddress", - "recipientAddress", - "withdrawAuthority", - "amount" - ] + "description": "The balance information for a stake account." }, - "v1alpha1StakingTarget": { + "v1StakingTarget": { "type": "object", "properties": { "validator": { - "$ref": "#/definitions/v1alpha1Validator", + "$ref": "#/definitions/v1Validator", "description": "A validator to stake to." }, "contract": { - "$ref": "#/definitions/v1alpha1Contract", + "$ref": "#/definitions/v1Contract", "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": { + "v1TxStepOutput": { "type": "object", "properties": { "unsignedTx": { @@ -1514,7 +1265,7 @@ "readOnly": true }, "state": { - "$ref": "#/definitions/v1alpha1TxStepOutputState", + "$ref": "#/definitions/v1TxStepOutputState", "description": "The state of the transaction step.", "readOnly": true }, @@ -1526,7 +1277,7 @@ }, "description": "The details of a transaction being constructed and broadcasted to the network." }, - "v1alpha1TxStepOutputState": { + "v1TxStepOutputState": { "type": "string", "enum": [ "STATE_UNSPECIFIED", @@ -1540,22 +1291,17 @@ "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." + "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_PENDING_EXT_BROADCAST: Tx is waiting to be externally broadcasted by the customer." }, - "v1alpha1Validator": { + "v1Validator": { "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" + "title": "The resource name of the Validator.\nFormat: protocols/{protocolName}/networks/{networkName}/stakingTargets/{validatorName}\nEx: protocols/solana/networks/testnet/stakingTargets/GkqYQysEGmuL6V2AJoNnWZUz2ZBGWhzQXsJiXm2CLKAN" }, "address": { "type": "string", @@ -1569,7 +1315,7 @@ }, "description": "A Validator resource represents an active validator for the given protocol network." }, - "v1alpha1ViewStakingContextResponse": { + "v1ViewStakingContextResponse": { "type": "object", "properties": { "address": { @@ -1577,17 +1323,22 @@ "description": "The address you are getting a staking context for." }, "ethereumKilnStakingContextDetails": { - "$ref": "#/definitions/v1alpha1EthereumKilnStakingContextDetails", + "$ref": "#/definitions/v1EthereumKilnStakingContextDetails", "description": "EthereumKiln staking context details." + }, + "solanaStakingContextDetails": { + "$ref": "#/definitions/v1SolanaStakingContextDetails", + "description": "Solana staking context details." } }, "description": "The response message for the ViewStakingContext request.", "required": [ "address", - "ethereumKilnStakingContextDetails" + "ethereumKilnStakingContextDetails", + "solanaStakingContextDetails" ] }, - "v1alpha1WaitStepOutput": { + "v1WaitStepOutput": { "type": "object", "properties": { "start": { @@ -1614,14 +1365,14 @@ "readOnly": true }, "state": { - "$ref": "#/definitions/v1alpha1WaitStepOutputState", + "$ref": "#/definitions/v1WaitStepOutputState", "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": { + "v1WaitStepOutputState": { "type": "string", "enum": [ "STATE_UNSPECIFIED", @@ -1632,7 +1383,7 @@ "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": { + "v1Workflow": { "type": "object", "properties": { "name": { @@ -1644,20 +1395,16 @@ "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", + "$ref": "#/definitions/v1SolanaStakingParameters", "description": "Solana staking parameters." }, "ethereumKilnStakingParameters": { - "$ref": "#/definitions/v1alpha1EthereumKilnStakingParameters", + "$ref": "#/definitions/v1EthereumKilnStakingParameters", "description": "EthereumKiln staking parameters." }, "state": { - "$ref": "#/definitions/v1alpha1WorkflowState", + "$ref": "#/definitions/v1WorkflowState", "description": "The current state of the workflow.", "readOnly": true }, @@ -1671,7 +1418,7 @@ "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1alpha1WorkflowStep" + "$ref": "#/definitions/v1WorkflowStep" }, "description": "The list of steps for this workflow.", "readOnly": true @@ -1702,12 +1449,11 @@ "description": "A Workflow resource.", "required": [ "action", - "polygonStakingParameters", "solanaStakingParameters", "ethereumKilnStakingParameters" ] }, - "v1alpha1WorkflowState": { + "v1WorkflowState": { "type": "string", "enum": [ "STATE_UNSPECIFIED", @@ -1715,17 +1461,13 @@ "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" + "description": "Example flow: A workflow with skip_broadcast = true leading to a successful completion.\n IN_PROGRESS -\u003e WAITING_FOR_EXT_BROADCAST -\u003e IN_PROGRESS -\u003e COMPLETED\n Example flow: A workflow with skip_broadcast = false leading to a successful completion.\n IN_PROGRESS -\u003e WAITING_FOR_SIGNING -\u003e IN_PROGRESS -\u003e COMPLETED\n Example flow: A workflow with skip_broadcast = false leading to a failure.\n IN_PROGRESS -\u003e WAITING_FOR_SIGNING -\u003e IN_PROGRESS -\u003e FAILED\n\n - 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_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" }, - "v1alpha1WorkflowStep": { + "v1WorkflowStep": { "type": "object", "properties": { "name": { @@ -1734,12 +1476,12 @@ "readOnly": true }, "txStepOutput": { - "$ref": "#/definitions/v1alpha1TxStepOutput", + "$ref": "#/definitions/v1TxStepOutput", "description": "The tx step output (e.g. transaction metadata such as unsigned tx, signed tx etc).", "readOnly": true }, "waitStepOutput": { - "$ref": "#/definitions/v1alpha1WaitStepOutput", + "$ref": "#/definitions/v1WaitStepOutput", "description": "The waiting details for any kind like how many checkpoints away for unbonding etc.", "readOnly": true } diff --git a/docs/openapi/rewards.swagger.json b/docs/openapi/rewards.swagger.json new file mode 100644 index 0000000..a1ae811 --- /dev/null +++ b/docs/openapi/rewards.swagger.json @@ -0,0 +1,617 @@ +{ + "swagger": "2.0", + "info": { + "title": "Coinbase Rewards API", + "description": "API that delivers crypto-forward onchain staking-related rewards data", + "version": "v1" + }, + "tags": [ + { + "name": "Reward", + "description": "A high-level view of an address's rewards aggregated over some period of time (ex: over an Epoch)." + }, + { + "name": "Stake", + "description": "A snapshot of an address's staking-related balance at a particular point in time. Feature coming soon." + }, + { + "name": "RewardService" + } + ], + "host": "api.developer.coinbase.com", + "basePath": "/rewards", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/v1/{name}": { + "get": { + "summary": "Returns a staking balance", + "description": "Returns a specific staking balance for an address on the specific protocol at a particular point in time.", + "operationId": "RewardService_GetStake", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1Stake" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "example": { + "code": 3, + "message": "Invalid stake ID. \u003cRemediation assistance here\u003e.", + "details": [] + } + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "example": "Unauthorized" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "example": { + "code": 3, + "message": "Internal server error.", + "details": [] + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "description": "The resource name of the stake to retrieve.\nFormat: protocols/{protocol}/stakes/{stake}", + "in": "path", + "required": true, + "type": "string", + "pattern": "protocols/[^/]+/stakes/[^/]+" + } + ], + "tags": [ + "Stake" + ] + } + }, + "/v1/{parent}/rewards": { + "get": { + "summary": "List and filter rewards", + "description": "Lists onchain rewards of an address for a specific protocol, with optional filters for time range, aggregation period, and more.", + "operationId": "RewardService_ListRewards", + "responses": { + "200": { + "description": "OK", + "schema": { + "example": { + "rewards": [ + { + "address": "beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar", + "epoch": "533", + "aggregationUnit": "epoch", + "periodStartTime": null, + "periodEndTime": "2023-11-16T00:13:44Z", + "totalEarnedNativeUnit": { + "amount": "224.7098145", + "exp": "9", + "ticker": "SOL", + "rawNumeric": "224709814509" + }, + "totalEarnedUsd": null, + "endingBalance": null, + "protocol": "solana" + }, + { + "address": "beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar", + "epoch": "532", + "aggregationUnit": "epoch", + "periodStartTime": null, + "periodEndTime": "2023-11-13T19:38:36Z", + "totalEarnedNativeUnit": { + "amount": "225.0794241", + "exp": "9", + "ticker": "SOL", + "rawNumeric": "225079424094" + }, + "totalEarnedUsd": null, + "endingBalance": null, + "protocol": "solana" + } + ], + "nextPageToken": "VAql-wtdiJWkWII9bJBDnE9oEc-8IlgU0DtKbxSDtBg=:1:1700241277" + } + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "example": { + "code": 3, + "message": "Filter validation failed. \u003cRemediation assistance here\u003e.", + "details": [] + } + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "example": "Unauthorized" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "example": { + "code": 3, + "message": "Internal server error.", + "details": [] + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The protocol that the rewards were earned on.\nThe response will only include rewards for the protocol specified here.", + "in": "path", + "required": true, + "type": "string", + "pattern": "protocols/[^/]+" + }, + { + "name": "pageSize", + "description": "The maximum number of items to return. Maximum size of this value is 500.\nIf user supplies a value \u003e 500, the API will truncate to 500.", + "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 correctly.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "filter", + "description": "[AIP-160](https://google.aip.dev/160) format compliant filter. Supported protocols are 'ethereum', 'solana', and 'cosmos'.\nSupplying other protocols will return an error.\n* **Ethereum**:\n - Fields:\n - `address` - A ethereum validator public key.\n - `epoch` - An ethereum epoch. Supports epoch comparisons (ex: `epoch \u003e= 1000 AND epoch \u003c= 2000`).\n - Example(s):\n - `\"address='0xac53512c39d0081ca4437c285305eb423f474e6153693c12fbba4a3df78bcaa3422b31d800c5bea71c1b017168a60474' AND epoch \u003e= 234640 AND epoch \u003c 234645\"`\n\n* **Solana**:\n - Fields:\n - `address` - A solana validator or delegator address.\n - `epoch` - A solana epoch. Supports epoch comparisons (ex: `epoch \u003e= 1000 AND epoch \u003c= 2000`).\n - Example(s):\n - `\"address='beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar' AND epoch \u003e= 440 AND epoch \u003c 450\"`\n\n* **Cosmos**:\n - Fields:\n - `address` - A cosmos validator or delegator address (ex: `cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en` and `cosmos1c4k24jzduc365kywrsvf5ujz4ya6mwymy8vq4q`)\n - `date` - A date in format 'YYYY-MM-DD'. Supports multiple comparisons (ex: `date \u003e= '2023-07-01' AND date \u003c= '2023-07-31'`).\n - Example(s):\n - `address='cosmos1mfduj0qax6ut8rd6cfc4j0ds06z0mwlhrljhqh' AND date = '2023-10-16'`", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Reward" + ] + } + }, + "/v1/{parent}/stakes": { + "get": { + "summary": "List and filter staking balances", + "description": "Lists staking balance of a protocol, with optional filters for time range and address.", + "operationId": "RewardService_ListStakes", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1ListStakesResponse" + } + }, + "400": { + "description": "The request attempted has invalid parameters", + "schema": { + "example": { + "code": 3, + "message": "Filter validation failed. \u003cRemediation assistance here\u003e.", + "details": [] + } + } + }, + "401": { + "description": "Returned if authentication information is invalid", + "schema": { + "example": "Unauthorized" + } + }, + "500": { + "description": "Returned when an internal server error happens.", + "schema": { + "example": { + "code": 3, + "message": "Internal server error.", + "details": [] + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "parent", + "description": "The protocol that the staking balance exists on.\nThe response will only include staking balances for the protocol specified here.", + "in": "path", + "required": true, + "type": "string", + "pattern": "protocols/[^/]+" + }, + { + "name": "pageSize", + "description": "The maximum number of items to return.", + "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" + }, + { + "name": "filter", + "description": "[AIP-160](https://google.aip.dev/160) filter", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Stake" + ] + } + } + }, + "definitions": { + "RewardRateCalculationMethods": { + "type": "string", + "enum": [ + "CALCULATION_METHODS_UNSPECIFIED", + "SOLO_STAKER", + "POOLED_STAKER", + "EPOCH_AUTO_COMPOUNDING", + "NO_AUTO_COMPOUNDING" + ], + "default": "CALCULATION_METHODS_UNSPECIFIED", + "description": "Representing the different methods of calculating yield.\n\n - CALCULATION_METHODS_UNSPECIFIED: Calculation method is unknown or unspecified.\n - SOLO_STAKER: A single Ethereum validator acting in isolation is currently not able to compound earned rewards because\nEthereum only allows validators to stake 32 ETH precisely.\nThis percentage yield is assuming that the rewards never compound, mimicking the behavior of a solo staker.\n - POOLED_STAKER: A pool of Ethereum validators of sufficient size is able to compound rewards almost immediately.\nThis percentage yield is assuming rewards compound immediately, mimicking the behavior of a sufficiently large pool.\n - EPOCH_AUTO_COMPOUNDING: A Solana delegator's staking rewards are staked (and therefore auto-compound) when rewards are paid out between epochs.\nThis percentage yield is assuming the rewards are auto-compounded on that schedule, mimicking a Solana delegator.\n - NO_AUTO_COMPOUNDING: A Solana validator's rewards accumulate in a separate account from the validator's active stake.\nThis percentage yield is assuming the rewards are not auto-compounded at any point, mimicking a Solana validator who never staked their rewards." + }, + "StakeDelegation": { + "type": "object", + "properties": { + "address": { + "type": "string", + "title": "Address associated to the delegation" + }, + "amount": { + "$ref": "#/definitions/v1AssetAmount", + "title": "Amount of delegation received or given" + }, + "commissionRate": { + "type": "string", + "title": "Commission rate for delegation" + } + }, + "description": "A single delegation from one address to another." + }, + "USDValueSource": { + "type": "string", + "enum": [ + "SOURCE_UNSPECIFIED", + "COINBASE_EXCHANGE" + ], + "default": "SOURCE_UNSPECIFIED", + "description": "The source of the USD price conversion.\n\n - SOURCE_UNSPECIFIED: The USD value source is unknown or unspecified.\n - COINBASE_EXCHANGE: The USD value source is the Coinbase exchange." + }, + "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" + } + } + } + }, + "v1AggregationUnit": { + "type": "string", + "enum": [ + "AGGREGATION_UNIT_UNSPECIFIED", + "EPOCH", + "DAY" + ], + "default": "AGGREGATION_UNIT_UNSPECIFIED", + "description": "The unit of time that the reward events were aggregated by.\n\n - AGGREGATION_UNIT_UNSPECIFIED: Aggregation unit is unknown or unspecified.\n - EPOCH: Indicates the rewards are aggregated by epoch. This means there will be a 'epoch' field displaying the epoch on this resource.\n - DAY: Indicates the rewards are aggregated by day. This means there will be a 'date' field displaying the date on this resource." + }, + "v1AssetAmount": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "title": "The amount of the asset in the most common denomination.\nEx: ETH (converted from gwei)\nEx: USD (converted from fractional pennies)", + "readOnly": true + }, + "exp": { + "type": "string", + "description": "The number of decimals needed to convert from the raw numeric value to the most\ncommon denomination.", + "readOnly": true + }, + "ticker": { + "type": "string", + "description": "The ticker of this asset (ex: USD, ETH, SOL).", + "readOnly": true + }, + "rawNumeric": { + "type": "string", + "description": "The raw, unadulterated numeric value.\nEx: Wei (in Ethereum) and Lamports (in Solana).", + "readOnly": true + } + }, + "description": "Amount encapsulation for a given asset." + }, + "v1ListRewardsResponse": { + "type": "object", + "properties": { + "rewards": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Reward" + }, + "description": "The rewards returned in this response.", + "readOnly": true + }, + "nextPageToken": { + "type": "string", + "description": "The page token the user must use in the next request if the next page is desired.", + "readOnly": true + } + }, + "description": "The response message for ListRewards." + }, + "v1ListStakesResponse": { + "type": "object", + "properties": { + "stakes": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Stake" + }, + "description": "The staking balances returned in this response.", + "readOnly": true + }, + "nextPageToken": { + "type": "string", + "description": "The page token the user must use in the next request if the next page is desired.", + "readOnly": true + } + }, + "description": "The response message for ListStakes." + }, + "v1ParticipantType": { + "type": "string", + "enum": [ + "PARTICIPANT_TYPE_UNSPECIFIED", + "DELEGATOR", + "VALIDATOR" + ], + "default": "PARTICIPANT_TYPE_UNSPECIFIED", + "description": "The participant type of a staking-related address.\n\n - PARTICIPANT_TYPE_UNSPECIFIED: The participant type is unknown.\n - DELEGATOR: Used when the onchain participant type is a delegator\n(i.e. someone who delegates the responsibilities of validating blocks to another address in return for a share of the rewards).\n - VALIDATOR: Used when the onchain participant type is a validator\n(i.e. an address that is directly responsible for performing validation of blocks)." + }, + "v1Reward": { + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "The address that earned this reward.", + "readOnly": true + }, + "epoch": { + "type": "string", + "format": "int64", + "description": "A unique identifier for the consensus-cycle of the blockchain.", + "readOnly": true + }, + "date": { + "type": "string", + "description": "The date of the reward in format 'YYYY-MM-DD' in UTC.", + "readOnly": true + }, + "aggregationUnit": { + "$ref": "#/definitions/v1AggregationUnit", + "description": "The unit of time that the reward events were rolled up by.\nCan be either \"epoch\" or \"daily\".", + "readOnly": true + }, + "periodStartTime": { + "type": "string", + "format": "date-time", + "description": "The starting time of this reward period. Returned when querying by epoch.\nTimestamps are in UTC, conforming to the RFC-3339 spec (e.g. 2023-11-13T19:38:36Z). UTC offsets are not currently supported.\nField currently unavailable. Coming soon.", + "readOnly": true + }, + "periodEndTime": { + "type": "string", + "format": "date-time", + "description": "The ending time of this reward period. Returned when querying by epoch.\nTimestamps are in UTC, conforming to the RFC-3339 spec (e.g. 2023-11-13T19:38:36Z). UTC offsets are not currently supported.", + "readOnly": true + }, + "totalEarnedNativeUnit": { + "$ref": "#/definitions/v1AssetAmount", + "description": "The amount earned in this time period in the native unit of the protocol.", + "readOnly": true + }, + "totalEarnedUsd": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1USDValue" + }, + "description": "The amount earned in this time period in USD. Calculated by getting each individual reward of this\ntime period and summing the USD value of each individual component. USD value is calculate at\nthe time each component was earned.", + "readOnly": true + }, + "endingBalance": { + "$ref": "#/definitions/v1Stake", + "description": "A snapshot of the staking balance the end of this period.\nField currently unavailable. Coming soon.", + "readOnly": true + }, + "protocol": { + "type": "string", + "description": "The protocol on which this reward was earned.", + "readOnly": true + } + }, + "description": "Rewards earned within a particular period of time." + }, + "v1RewardRate": { + "type": "object", + "properties": { + "percentage": { + "type": "string", + "description": "The percentage rate of rewards calculation. Will include two digits after the decimal (ex: 3.05).", + "readOnly": true + }, + "calculatedTime": { + "type": "string", + "format": "date-time", + "description": "The time at which this yield calculation was calculated.\nTimestamps are in UTC, conforming to the RFC-3339 spec (e.g. 2023-11-13T19:38:36Z). UTC offsets are not currently supported.", + "readOnly": true + }, + "calculationMethod": { + "$ref": "#/definitions/RewardRateCalculationMethods", + "description": "The method used to calculate this yield. This could include information about which\nrewards we're including in the calculation, how we're estimating the compounding period, etc.", + "readOnly": true + } + }, + "description": "Reward yield calculation at a given point in time." + }, + "v1Stake": { + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "The address of the staking balance.", + "readOnly": true + }, + "evaluationTime": { + "type": "string", + "format": "date-time", + "description": "The time at which this balance was evaluated.\nTimestamps are in UTC, conforming to the RFC-3339 spec (e.g. 2023-11-13T19:38:36Z). UTC offsets are not currently supported.", + "readOnly": true + }, + "bondedStake": { + "$ref": "#/definitions/v1AssetAmount", + "description": "The total amount of stake that is actively earning rewards to this address.\nIncludes any delegated stake and self-stake.\nFor delegators, this would be only the amount delegated to a validator in most cases.\nOnly includes stake that is *actively contributing to rewards and can't be reduced\nwithout affecting the rewards dynamics*.\n\nPending inactive stake is included.\nPending active stake is not included.", + "readOnly": true + }, + "totalDelegationReceived": { + "$ref": "#/definitions/v1AssetAmount", + "description": "The amount of stake that this address receives from other addresses.\nFor most delegators, this will be 0.", + "readOnly": true + }, + "delegationsReceived": { + "$ref": "#/definitions/StakeDelegation", + "title": "The list of individual delegations this address has received from other addresses", + "readOnly": true + }, + "delegationsGiven": { + "$ref": "#/definitions/StakeDelegation", + "description": "The amount that this address stakes to another address.", + "readOnly": true + }, + "rewardRateCalculations": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1RewardRate" + }, + "description": "An estimated yield of this address.", + "readOnly": true + }, + "participantType": { + "$ref": "#/definitions/v1ParticipantType", + "description": "The participant type at the time of evaluation (i.e. validator, delegator).", + "readOnly": true + }, + "protocol": { + "type": "string", + "description": "The protocol on which this staking balance exists.", + "readOnly": true + }, + "unbondedBalance": { + "$ref": "#/definitions/v1AssetAmount", + "description": "The amount of stake that is not actively earning rewards to this address.\nThis amount includes any native token balance that is under the domain and control of the address in question,\nbut is not actively staked.\n\nPending active stake would be included here.", + "readOnly": true + } + }, + "description": "The representation of a staking activity at a particular point in time." + }, + "v1USDValue": { + "type": "object", + "properties": { + "source": { + "$ref": "#/definitions/USDValueSource", + "description": "The source of the USD price conversion. Could be internal to Coinbase, and external source, or any other source.", + "readOnly": true + }, + "conversionTime": { + "type": "string", + "format": "date-time", + "description": "The timestamp at which the USD value was sourced to convert the value into USD.\nThis value is as close to the time the reward was earned as possible.\nTimestamps are in UTC, conforming to the RFC-3339 spec (e.g. 2023-11-13T19:38:36Z). UTC offsets are not currently supported.", + "readOnly": true + }, + "amount": { + "$ref": "#/definitions/v1AssetAmount", + "description": "The USD value of the reward at the conversion time..", + "readOnly": true + } + }, + "description": "Information regarding the USD value of a reward, with necessary context and metadata." + } + } +} diff --git a/src/examples/public/example_e2e_staking.ts b/examples/ethereum/create-and-process-workflow.ts similarity index 57% rename from src/examples/public/example_e2e_staking.ts rename to examples/ethereum/create-and-process-workflow.ts index 3e31f73..d5a671f 100644 --- a/src/examples/public/example_e2e_staking.ts +++ b/examples/ethereum/create-and-process-workflow.ts @@ -1,41 +1,41 @@ -import { TxSignerFactory } from "../../signers"; +import { TxSignerFactory } from '../../src/signers'; import { - StakingServiceClient, + StakingClient, workflowHasFinished, workflowWaitingForSigning, workflowWaitingForExternalBroadcast, isTxStepOutput, isWaitStepOutput, -} from "../../client/staking-service-client"; -import { Workflow } from "../../gen/coinbase/staking/v1alpha1/workflow.pb"; -import { calculateTimeDifference } from "../../utils/date"; +} from '../../src/client/staking-client'; +import { Workflow } from '../../src/gen/coinbase/staking/orchestration/v1/workflow.pb'; +import { calculateTimeDifference } from '../../src/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 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 = '0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b'; // replace with your integration address +const amount: string = '123'; // replace with your amount +const network: string = 'holesky'; // replace with your network -const client = new StakingServiceClient(); +const client = new StakingClient(); -const signer = TxSignerFactory.getSigner("ethereum"); +const signer = TxSignerFactory.getSigner('ethereum'); async function stakePartialEth(): Promise { - if (projectId === "" || privateKey === "" || stakerAddress === "") { + if (projectId === '' || privateKey === '' || stakerAddress === '') { throw new Error( - "Please set the projectId, privateKey and stakerAddress variables in this file", + 'Please set the projectId, privateKey and stakerAddress variables in this file', ); } - let unsignedTx = ""; + 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( + workflow = await client.Ethereum.stake( projectId, network, false, @@ -44,19 +44,22 @@ async function stakePartialEth(): Promise { amount, ); - workflowId = workflow.name?.split("/").pop() || ""; - if (workflowId == null || workflowId === "") { - throw new Error("Unexpected workflow state. workflowId is null"); + 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"); + 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}`); + console.log('Workflow created %s ...', workflow.name); + } catch (error) { + if (error instanceof Error) { + throw new Error(`Error creating workflow: ${error.message}`); + } + throw new Error(`Error creating workflow`); } // Loop until the workflow has reached an end state. @@ -68,25 +71,28 @@ async function stakePartialEth(): Promise { // 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) { + } catch (error) { // TODO: add retry logic for network errors - throw new Error(`Error getting workflow: ${error.message}`); + if (error instanceof Error) { + throw new Error(`Error creating 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 ..."); + 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); + console.log('Signing unsigned tx %s ...', unsignedTx); const signedTx = await signer.signTransaction(privateKey, unsignedTx); - console.log("Returning back signed tx %s ...", signedTx); + + console.log('Returning back signed tx %s ...', signedTx); workflow = await client.performWorkflowStep( projectId, @@ -96,12 +102,12 @@ async function stakePartialEth(): Promise { ); } 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 ...", + '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); + console.log('Workflow completed with state %s ...', workflow.state); break; } @@ -111,26 +117,27 @@ async function stakePartialEth(): Promise { async function printWorkflowProgressDetails(workflow: Workflow): Promise { if (workflow.steps == null || workflow.steps.length === 0) { - console.log("Waiting for steps to be created ..."); + 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 = ""; + 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}` + stepDetails = `state: ${step.waitStepOutput?.state}} current: ${step.waitStepOutput?.current}} target: ${step.waitStepOutput?.target}`; } else { - throw new Error("Encountered unexpected workflow step type"); + throw new Error('Encountered unexpected workflow step type'); } const runtime = calculateTimeDifference( @@ -140,7 +147,7 @@ async function printWorkflowProgressDetails(workflow: Workflow): Promise { if (workflowHasFinished(workflow)) { console.log( - "Workflow reached end state - step name: %s %s workflow state: %s runtime: %d seconds", + 'Workflow reached end state - step name: %s %s workflow state: %s runtime: %d seconds', step.name, stepDetails, workflow.state, @@ -148,7 +155,7 @@ async function printWorkflowProgressDetails(workflow: Workflow): Promise { ); } else { console.log( - "Waiting for workflow to finish - step name: %s %s workflow state: %s runtime: %d seconds", + 'Waiting for workflow to finish - step name: %s %s workflow state: %s runtime: %d seconds', step.name, stepDetails, workflow.state, @@ -159,8 +166,8 @@ async function printWorkflowProgressDetails(workflow: Workflow): Promise { stakePartialEth() .then(() => { - console.log("Done staking eth"); + console.log('Done staking eth'); }) .catch((error) => { - console.error("Error staking eth: ", error.message); + console.error('Error staking eth: ', error.message); }); diff --git a/examples/ethereum/create-workflow.ts b/examples/ethereum/create-workflow.ts new file mode 100644 index 0000000..d1595d0 --- /dev/null +++ b/examples/ethereum/create-workflow.ts @@ -0,0 +1,51 @@ +import { StakingClient } from '../../src/client/staking-client'; +import { Workflow } from '../../src/gen/coinbase/staking/orchestration/v1/workflow.pb'; + +const projectId: string = ''; // replace with your project id +const stakerAddress: string = ''; // replace with your staker address +const integrationAddress: string = '0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b'; // replace with your integration address +const amount: string = '123'; // replace with your amount +const network: string = 'holesky'; // replace with your network + +const client = new StakingClient(); + +async function stakePartialEth(): Promise { + if (projectId === '' || stakerAddress === '') { + throw new Error( + 'Please set the projectId and stakerAddress variables in this file', + ); + } + + let workflow: Workflow = {} as Workflow; + + try { + // Create a new eth kiln stake workflow + workflow = await client.Ethereum.stake( + projectId, + network, + true, + stakerAddress, + integrationAddress, + amount, + ); + + console.log('Workflow created %s ...', workflow.name); + } catch (error) { + let errorMessage = ''; + + if (error instanceof Error) { + errorMessage = error.message; + } + throw new Error(`Error creating workflow: ${errorMessage}`); + } +} + +stakePartialEth() + .then(() => { + console.log('Done creating eth staking workflow'); + }) + .catch((error) => { + if (error instanceof Error) { + console.error('Error creating eth staking workflow: ', error.message); + } + }); diff --git a/examples/ethereum/list-rewards.ts b/examples/ethereum/list-rewards.ts new file mode 100644 index 0000000..c45ec0b --- /dev/null +++ b/examples/ethereum/list-rewards.ts @@ -0,0 +1,15 @@ +import { StakingClient } from '../../src/client/staking-client'; + +// Defines which address and rewards we want to see +const address: string = + '0xac53512c39d0081ca4437c285305eb423f474e6153693c12fbba4a3df78bcaa3422b31d800c5bea71c1b017168a60474'; +const filter: string = `address='${address}' AND period_end_time > '2024-02-25T00:00:00Z' AND period_end_time < '2024-02-27T00:00:00Z'`; + +const client = new StakingClient(); + +// Loops through rewards array and prints each reward +client.Ethereum.listRewards(filter).then((resp) => { + resp.rewards!.forEach((reward) => { + console.log(JSON.stringify(reward, null, 2)); + }); +}); diff --git a/examples/ethereum/list-stakes.ts b/examples/ethereum/list-stakes.ts new file mode 100644 index 0000000..8835d32 --- /dev/null +++ b/examples/ethereum/list-stakes.ts @@ -0,0 +1,38 @@ +import { StakingClient } from '../../src/client/staking-client'; + +// TODO: Replace address as per your requirement. +const address: string = + '0xac53512c39d0081ca4437c285305eb423f474e6153693c12fbba4a3df78bcaa3422b31d800c5bea71c1b017168a60474'; + +const client = new StakingClient(); + +async function listStakes(): Promise { + if (address === '') { + throw new Error('Please set the address variable in this file'); + } + + const filter: string = `address='${address}'`; + + try { + // List ethereum staking balances + let resp = await client.Ethereum.listStakes(filter); + + let count = 0; + + // Loop through rewards array and print each reward + resp.stakes!.forEach((stake) => { + count++; + const marshaledStake = JSON.stringify(stake); + + console.log(`[${count}] Stake details: ${marshaledStake}`); + }); + } catch (error) { + if (error instanceof Error) { + throw new Error(`Error listing staking balances: ${error.message}`); + } + } +} + +listStakes().catch((error) => { + console.error('Error listing solana staking balances: ', error.message); +}); diff --git a/examples/example.ts b/examples/example.ts new file mode 100644 index 0000000..3a161db --- /dev/null +++ b/examples/example.ts @@ -0,0 +1,15 @@ +import { StakingClient } from '../src/client/staking-client'; + +const client = new StakingClient(); + +client.listProtocols().then((response) => { + console.log(response); +}); + +client.listNetworks('ethereum_kiln').then((response) => { + console.log(response); +}); + +client.listActions('ethereum_kiln', 'holesky').then((response) => { + console.log(response); +}); diff --git a/examples/solana/create-workflow.ts b/examples/solana/create-workflow.ts new file mode 100644 index 0000000..72898c1 --- /dev/null +++ b/examples/solana/create-workflow.ts @@ -0,0 +1,182 @@ +import { + StakingClient, + workflowHasFinished, + workflowWaitingForSigning, + workflowWaitingForExternalBroadcast, + isTxStepOutput, + isWaitStepOutput, +} from '../../src/client/staking-client'; +import { Workflow } from '../../src/gen/coinbase/staking/orchestration/v1/workflow.pb'; +import { calculateTimeDifference } from '../../src/utils/date'; + +const projectId: string = ''; // replace with your project id +const walletAddress: string = ''; // replace with your wallet address +const validatorAddress: string = 'beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar'; // replace with your validator address +const amount: string = '100000000'; // replace with your amount. For solana it should be >= 0.1 SOL +const network: string = 'mainnet'; // replace with your network + +const client = new StakingClient(); + +async function stakeSolana(): Promise { + if (projectId === '' || walletAddress === '') { + throw new Error( + 'Please set the projectId and stakerAddress variables in this file', + ); + } + + let unsignedTx = ''; + let workflow: Workflow = {} as Workflow; + let currentStepId: number | undefined; + let workflowId: string; + + try { + // Create a new solana stake workflow + workflow = await client.Solana.stake( + projectId, + network, + true, + walletAddress, + validatorAddress, + 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) { + if (error instanceof Error) { + throw new Error(`Error creating workflow: ${error.message}`); + } + throw new Error(`Error creating workflow`); + } + + // 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) { + // TODO: add retry logic for network errors + if (error instanceof Error) { + throw new Error(`Error getting workflow: ${error.message}`); + } + } + + await printWorkflowProgressDetails(workflow); + + if (workflowWaitingForSigning(workflow)) { + console.log( + 'Please sign this unsigned tx %s and return back the signed tx via the PerformWorkflowStep API ...', + unsignedTx, + ); + + /* This is where a customer needs to implement signing logic and return back signedTx + workflow = await client.performWorkflowStep( + projectId, + workflowId, + currentStepId, + signedTx, + ); + */ + break; + } else if (workflowWaitingForExternalBroadcast(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( + 'Please sign and broadcast this unsigned tx %s externally and return back the tx hash via the PerformWorkflowStep API ...', + unsignedTx, + ); + + /* This is where a customer needs to implement signing, broadcasting logic and return back broadcasted tx hash + workflow = await client.performWorkflowStep( + projectId, + workflowId, + currentStepId, + txHash, + ); + */ + + 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, + ); + } +} + +stakeSolana() + .then(() => { + console.log('Done staking sol'); + }) + .catch((error) => { + console.error('Error staking sol: ', error.message); + }); diff --git a/examples/solana/list-rewards.ts b/examples/solana/list-rewards.ts new file mode 100644 index 0000000..6a66aa0 --- /dev/null +++ b/examples/solana/list-rewards.ts @@ -0,0 +1,37 @@ +import { StakingClient } from '../../src/client/staking-client'; + +// TODO: Replace address as per your requirement. +const address: string = 'beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar'; + +const client = new StakingClient(); + +async function listRewards(): Promise { + if (address === '') { + throw new Error('Please set the address variable in this file'); + } + + const filter: string = `address='${address}'`; + + try { + // List solana rewards + let resp = await client.Solana.listRewards(filter); + + let count = 0; + + // Loop through rewards array and print each reward + resp.rewards!.forEach((reward) => { + count++; + const marshaledReward = JSON.stringify(reward); + + console.log(`[${count}] Reward details: ${marshaledReward}`); + }); + } catch (error) { + if (error instanceof Error) { + throw new Error(`Error listing solana rewards: ${error.message}`); + } + } +} + +listRewards().catch((error) => { + console.error('Error listing solana rewards: ', error.message); +}); diff --git a/package-lock.json b/package-lock.json index f738c69..849061f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,30 @@ { "name": "@coinbase/staking-client-library-ts", - "version": "0.4.2", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@coinbase/staking-client-library-ts", - "version": "0.4.2", + "version": "0.5.0", "license": "Apache-2.0", "dependencies": { "@ethereumjs/tx": "^5.1.0", "node-jose": "^2.2.0" }, "devDependencies": { + "@types/node": "^20.11.0", "@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", + "eslint-plugin-filename-rules": "^1.3.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-unicorn": "^51.0.1", "prettier": "^3.0.3", + "prettier-eslint": "^16.0.0", "rimraf": "^3.0.2", "typescript": "^5.2.2" } @@ -33,6 +38,114 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "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", @@ -58,9 +171,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -81,9 +194,9 @@ } }, "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==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -152,13 +265,13 @@ } }, "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==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -179,11 +292,23 @@ } }, "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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@noble/curves": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", @@ -241,19 +366,11 @@ "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==", + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "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" }, @@ -294,6 +411,12 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -301,10 +424,13 @@ "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 + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-jose": { "version": "1.1.10", @@ -315,6 +441,12 @@ "@types/node": "*" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -357,15 +489,15 @@ } }, "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==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "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", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -384,6 +516,105 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "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/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.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/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "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", @@ -510,10 +741,16 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -619,27 +856,6 @@ "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", @@ -662,6 +878,38 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -685,16 +933,13 @@ "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==", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, "engines": { - "node": ">=12" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -709,6 +954,26 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -725,6 +990,42 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -743,12 +1044,34 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "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/core-js-compat": { + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", + "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/crc": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", @@ -802,74 +1125,49 @@ "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==", + "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": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" + "path-type": "^4.0.0" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/default-browser-id": { + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.0.0" } }, - "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==", + "node_modules/electron-to-chromium": { + "version": "1.4.721", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.721.tgz", + "integrity": "sha512-k1x2r6foI8iJOp+1qTxbbrrWMsOiHkzGBYwYigaq+apO1FSqtn44KTo3Sy69qt7CRr7149zTcsDvH7MUKsOuIQ==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" + "is-arrayish": "^0.2.1" } }, "node_modules/es6-promise": { @@ -877,6 +1175,15 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -890,18 +1197,19 @@ } }, "node_modules/eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "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", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -955,24 +1263,82 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-filename-rules": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-filename-rules/-/eslint-plugin-filename-rules-1.3.1.tgz", + "integrity": "sha512-kBMxGFvK3QrRBHMurhFSNa+PFdszezVtBV6egg39TDzlj6D4jL3Xx6oyNjm5xE4C+TdQUBzWwymHJHBPyxOreA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "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==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.8.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/prettier" + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", + "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -984,6 +1350,39 @@ } } }, + "node_modules/eslint-plugin-unicorn": { + "version": "51.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", + "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.5.4", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -1000,6 +1399,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", @@ -1082,29 +1505,6 @@ "@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", @@ -1232,16 +1632,13 @@ "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==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { @@ -1277,9 +1674,9 @@ } }, "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==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1317,6 +1714,27 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1326,15 +1744,24 @@ "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==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=14.18.0" + "node": ">= 0.4" } }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1388,6 +1815,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1404,21 +1840,39 @@ "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==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, - "bin": { - "is-docker": "cli.js" + "dependencies": { + "builtin-modules": "^3.3.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1440,24 +1894,6 @@ "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", @@ -1476,51 +1912,18 @@ "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-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1533,12 +1936,30 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "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-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "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", @@ -1573,6 +1994,12 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1599,6 +2026,93 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -1616,12 +2130,6 @@ "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", @@ -1644,16 +2152,13 @@ "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==", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/minimatch": { @@ -1704,31 +2209,31 @@ "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==", + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "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" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "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==", + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "semver": "bin/semver" } }, "node_modules/once": { @@ -1740,39 +2245,6 @@ "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", @@ -1820,6 +2292,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -1837,6 +2318,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1864,6 +2363,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1891,6 +2396,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1915,6 +2429,41 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-eslint": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.3.0.tgz", + "integrity": "sha512-Lh102TIFCr11PJKUMQ2kwNmxGhTsv/KzUg9QYF2Gkw259g/kPgndZDWavk7/ycbRvj2oz4BPZ1gCU8bhfZH/Xg==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^6.7.5", + "common-tags": "^1.4.0", + "dlv": "^1.1.0", + "eslint": "^8.7.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^3.0.1", + "pretty-format": "^29.7.0", + "require-relative": "^0.8.7", + "typescript": "^5.2.2", + "vue-eslint-parser": "^9.1.0" + }, + "engines": { + "node": ">=16.10.0" + }, + "peerDependencies": { + "prettier-plugin-svelte": "^3.0.0", + "svelte-eslint-parser": "*" + }, + "peerDependenciesMeta": { + "prettier-plugin-svelte": { + "optional": true + }, + "svelte-eslint-parser": { + "optional": true + } + } + }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", @@ -1927,6 +2476,32 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -1936,9 +2511,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -1964,142 +2539,211 @@ } ] }, - "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==", + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=8" } }, - "node_modules/run-applescript": { + "node_modules/read-pkg-up/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "execa": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, "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==", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "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" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=8" } }, - "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==", + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=8" } }, - "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==", + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "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==", + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "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==", + "node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", "dev": true, "dependencies": { - "path-key": "^3.0.0" + "jsesc": "~0.5.0" }, - "engines": { - "node": ">=8" + "bin": { + "regjsparser": "bin/parser" } }, - "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==", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=6" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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": ">=6" + "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-parallel": { @@ -2161,12 +2805,6 @@ "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", @@ -2176,6 +2814,38 @@ "node": ">=8" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2188,16 +2858,16 @@ "node": ">=8" } }, - "node_modules/strip-final-newline": { + "node_modules/strip-indent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "min-indent": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, "node_modules/strip-json-comments": { @@ -2224,14 +2894,26 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -2246,18 +2928,6 @@ "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", @@ -2325,13 +2995,40 @@ "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==", + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "engines": { - "node": ">=8" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, "node_modules/uri-js": { @@ -2351,6 +3048,40 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", + "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 1b84bc1..11c12a0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@coinbase/staking-client-library-ts", - "version": "0.4.2", - "description": "Coinbase Cloud Staking API Typescript Library", + "version": "0.5.0", + "description": "Coinbase Staking API Typescript Library", "repository": "https://github.com/coinbase/staking-client-library-ts.git", "license": "Apache-2.0", "scripts": { @@ -9,7 +9,7 @@ "build": "npm run clean && tsc", "prepare": "npm run build", "lint": "eslint . --ext .ts --ignore-pattern '/dist/*/*'", - "lintfix": "eslint . --ext .ts --fix --ignore-pattern '/dist/*/*'" + "lint-fix": "eslint . --ext .ts --fix --ignore-pattern '/dist/*/*'" }, "publishConfig": { "access": "public", @@ -24,13 +24,18 @@ "@ethereumjs/tx": "^5.1.0" }, "devDependencies": { + "@types/node": "^20.11.0", "@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", + "eslint-plugin-filename-rules": "^1.3.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-unicorn": "^51.0.1", "prettier": "^3.0.3", + "prettier-eslint": "^16.0.0", "typescript": "^5.2.2", "rimraf": "^3.0.2" }, diff --git a/src/auth/index.ts b/src/auth/index.ts index ccb2171..3b22df7 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1,10 +1,10 @@ -import { readFileSync } from "fs"; -import { JWK, JWS } from "node-jose"; +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-----"; +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. @@ -14,10 +14,10 @@ const pemFooter = "-----END EC PRIVATE KEY-----"; */ export const buildJWT = async ( url: string, - method = "GET", + method = 'GET', ): Promise => { - const keyFile = readFileSync(".coinbase_cloud_api_key.json", { - encoding: "utf8", + const keyFile = readFileSync('.coinbase_cloud_api_key.json', { + encoding: 'utf8', }); const apiKey: APIKey = JSON.parse(keyFile); @@ -25,18 +25,18 @@ export const buildJWT = async ( let privateKey: JWK.Key; try { - privateKey = await JWK.asKey(pemPrivateKey, "pem"); - if (privateKey.kty !== "EC") { - throw new Error("Not an EC private key"); + 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", + alg: 'ES256', kid: apiKey.name, - typ: "JWT", + typ: 'JWT', nonce: nonce(), }; @@ -45,18 +45,18 @@ export const buildJWT = async ( const claims: APIKeyClaims = { sub: apiKey.name, - iss: "coinbase-cloud", + 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"); + const payload = Buffer.from(JSON.stringify(claims)).toString('utf8'); try { const result = await JWS.createSign( - { format: "compact", fields: header }, + { format: 'compact', fields: header }, privateKey, ) .update(payload) @@ -103,7 +103,7 @@ interface APIKeyClaims { */ const extractPemKey = (privateKeyString: string): string => { // Remove all newline characters - privateKeyString = privateKeyString.replace(/\n/g, ""); + privateKeyString = privateKeyString.replace(/\n/g, ''); // If the string starts with the standard PEM header and footer, return as is. if ( @@ -119,12 +119,13 @@ const extractPemKey = (privateKeyString: string): string => { ); 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"); + throw new Error('wrong format of API private key'); }; /** @@ -132,8 +133,9 @@ const extractPemKey = (privateKeyString: string): string => { * @returns The generated nonce. */ const nonce = (): string => { - const range = "0123456789"; - let result = ""; + const range = '0123456789'; + let result = ''; + for (let i = 0; i < 16; i++) { result += range.charAt(Math.floor(Math.random() * range.length)); } @@ -142,12 +144,12 @@ const nonce = (): string => { }; const getAudience = (url: string): string => { - if (url.indexOf("staking") > -1) { - return "staking"; - } else if (url.indexOf("rewards") > -1) { - return "rewards-reporting"; + if (url.indexOf('staking') > -1) { + return 'staking'; + } else if (url.indexOf('rewards') > -1) { + return 'rewards-reporting'; } else { - return "unknown"; + return 'unknown'; } }; @@ -157,8 +159,9 @@ export const customFetch = async ( ): Promise => { // remove query parameters let url = input.toString(); - if (url.indexOf("?") > -1) { - url = url.substring(0, url.indexOf("?")); + + if (url.indexOf('?') > -1) { + url = url.substring(0, url.indexOf('?')); } const token = await buildJWT(url, init?.method); const params = { diff --git a/src/client/protocols/ethereum_kiln_staking.ts b/src/client/protocols/ethereum-kiln-staking.ts similarity index 69% rename from src/client/protocols/ethereum_kiln_staking.ts rename to src/client/protocols/ethereum-kiln-staking.ts index c9b4630..5ca7f65 100644 --- a/src/client/protocols/ethereum_kiln_staking.ts +++ b/src/client/protocols/ethereum-kiln-staking.ts @@ -1,17 +1,25 @@ -import { StakingServiceClient } from "../staking-service-client"; +import { StakingClient } from '../staking-client'; import { CreateWorkflowRequest, Workflow, -} from "../../gen/coinbase/staking/v1alpha1/workflow.pb"; +} from '../../gen/coinbase/staking/orchestration/v1/workflow.pb'; import { ViewStakingContextRequest, ViewStakingContextResponse, -} from "../../gen/coinbase/staking/v1alpha1/staking_context.pb"; +} from '../../gen/coinbase/staking/orchestration/v1/staking_context.pb'; +import { + ListRewardsRequest, + ListRewardsResponse, +} from '../../gen/coinbase/staking/rewards/v1/reward.pb'; +import { + ListStakesRequest, + ListStakesResponse, +} from '../../gen/coinbase/staking/rewards/v1/stake.pb'; -export class EthereumKiln { - private parent: StakingServiceClient; +export class Ethereum { + private parent: StakingClient; - constructor(parent: StakingServiceClient) { + constructor(parent: StakingClient) { this.parent = parent; } @@ -34,7 +42,7 @@ export class EthereumKiln { integratorContractAddress: integratorContractAddress, amount: { value: amount, - currency: "ETH", + currency: 'ETH', }, }, }, @@ -63,7 +71,7 @@ export class EthereumKiln { integratorContractAddress: integratorContractAddress, amount: { value: amount, - currency: "ETH", + currency: 'ETH', }, }, }, @@ -112,4 +120,34 @@ export class EthereumKiln { return this.parent.viewStakingContext(req); } + + async listRewards( + filter: string, + pageSize: number = 100, + pageToken?: string, + ): Promise { + const req: ListRewardsRequest = { + parent: 'protocols/ethereum', + filter: filter, + pageSize: pageSize, + pageToken: pageToken, + }; + + return this.parent.listRewards('ethereum', req); + } + + async listStakes( + filter: string, + pageSize: number = 100, + pageToken?: string, + ): Promise { + const req: ListStakesRequest = { + parent: 'protocols/ethereum', + filter: filter, + pageSize: pageSize, + pageToken: pageToken, + }; + + return this.parent.listStakes('ethereum', req); + } } diff --git a/src/client/protocols/solana-staking.ts b/src/client/protocols/solana-staking.ts new file mode 100644 index 0000000..ca2dfc9 --- /dev/null +++ b/src/client/protocols/solana-staking.ts @@ -0,0 +1,149 @@ +import { StakingClient } from '../staking-client'; +import { + CreateWorkflowRequest, + Workflow, +} from '../../gen/coinbase/staking/orchestration/v1/workflow.pb'; +import { + ViewStakingContextRequest, + ViewStakingContextResponse, +} from '../../gen/coinbase/staking/orchestration/v1/staking_context.pb'; +import { + ListRewardsRequest, + ListRewardsResponse, +} from '../../gen/coinbase/staking/rewards/v1/reward.pb'; +import { + ListStakesRequest, + ListStakesResponse, +} from '../../gen/coinbase/staking/rewards/v1/stake.pb'; + +export class Solana { + private parent: StakingClient; + + constructor(parent: StakingClient) { + this.parent = parent; + } + + async stake( + projectId: string, + network: string, + skipBroadcast: boolean = false, + walletAddress: string, + validatorAddress: string, + amount: string, + ): Promise { + const req: CreateWorkflowRequest = { + parent: `projects/${projectId}`, + workflow: { + action: `protocols/solana/networks/${network}/actions/stake`, + skipBroadcast: skipBroadcast, + solanaStakingParameters: { + stakeParameters: { + walletAddress: walletAddress, + validatorAddress: validatorAddress, + amount: { + value: amount, + currency: 'SOL', + }, + }, + }, + }, + }; + + return this.parent.createWorkflow(projectId, req); + } + + async unstake( + projectId: string, + network: string, + skipBroadcast: boolean = false, + walletAddress: string, + stakeAccountAddress: string, + amount: string, + ): Promise { + const req: CreateWorkflowRequest = { + parent: `projects/${projectId}`, + workflow: { + action: `protocols/solana/networks/${network}/actions/unstake`, + skipBroadcast: skipBroadcast, + solanaStakingParameters: { + unstakeParameters: { + walletAddress: walletAddress, + stakeAccountAddress: stakeAccountAddress, + amount: { + value: amount, + currency: 'SOL', + }, + }, + }, + }, + }; + + return this.parent.createWorkflow(projectId, req); + } + + async claimStake( + projectId: string, + network: string, + skipBroadcast: boolean = false, + walletAddress: string, + stakeAccountAddress: string, + ): Promise { + const req: CreateWorkflowRequest = { + parent: `projects/${projectId}`, + workflow: { + action: `protocols/solana/networks/${network}/actions/claim_stake`, + skipBroadcast: skipBroadcast, + solanaStakingParameters: { + claimStakeParameters: { + walletAddress: walletAddress, + stakeAccountAddress: stakeAccountAddress, + }, + }, + }, + }; + + return this.parent.createWorkflow(projectId, req); + } + + async viewStakingContext( + address: string, + network: string, + ): Promise { + const req: ViewStakingContextRequest = { + address: address, + network: `protocols/solana/networks/${network}`, + }; + + return this.parent.viewStakingContext(req); + } + + async listRewards( + filter: string, + pageSize: number = 100, + pageToken?: string, + ): Promise { + const req: ListRewardsRequest = { + parent: 'protocols/solana', + filter: filter, + pageSize: pageSize, + pageToken: pageToken, + }; + + return this.parent.listRewards('solana', req); + } + + async listStakes( + filter: string, + pageSize: number = 100, + pageToken?: string, + ): Promise { + const req: ListStakesRequest = { + parent: 'protocols/solana', + filter: filter, + pageSize: pageSize, + pageToken: pageToken, + }; + + return this.parent.listStakes('solana', req); + } +} diff --git a/src/client/staking-service-client.ts b/src/client/staking-client.ts similarity index 52% rename from src/client/staking-service-client.ts rename to src/client/staking-client.ts index d0cd6cb..e4a1f23 100644 --- a/src/client/staking-service-client.ts +++ b/src/client/staking-client.ts @@ -1,43 +1,53 @@ -import { StakingService } from "../gen/coinbase/staking/v1alpha1/api.pb"; +import { StakingService } from '../gen/coinbase/staking/orchestration/v1/api.pb'; +import { RewardService } from '../gen/coinbase/staking/rewards/v1/reward_service.pb'; import { ListProtocolsRequest, ListProtocolsResponse, -} from "../gen/coinbase/staking/v1alpha1/protocol.pb"; +} from '../gen/coinbase/staking/orchestration/v1/protocol.pb'; import { ListNetworksRequest, ListNetworksResponse, -} from "../gen/coinbase/staking/v1alpha1/network.pb"; +} from '../gen/coinbase/staking/orchestration/v1/network.pb'; import { ListActionsRequest, ListActionsResponse, -} from "../gen/coinbase/staking/v1alpha1/action.pb"; -import * as fm from "../gen/fetch.pb"; -import { buildJWT } from "../auth"; +} from '../gen/coinbase/staking/orchestration/v1/action.pb'; +import * as fm from '../gen/fetch.pb'; +import { buildJWT } from '../auth'; import { ViewStakingContextRequest, ViewStakingContextResponse, -} from "../gen/coinbase/staking/v1alpha1/staking_context.pb"; +} from '../gen/coinbase/staking/orchestration/v1/staking_context.pb'; import { CreateWorkflowRequest, GetWorkflowRequest, ListWorkflowsRequest, ListWorkflowsResponse, PerformWorkflowStepRequest, - RefreshWorkflowStepRequest, Workflow, WorkflowState, WorkflowStep, TxStepOutput, WaitStepOutput, -} from "../gen/coinbase/staking/v1alpha1/workflow.pb"; +} from '../gen/coinbase/staking/orchestration/v1/workflow.pb'; +import { + ListRewardsRequest, + ListRewardsResponse, +} from '../gen/coinbase/staking/rewards/v1/reward.pb'; +import { + ListStakesRequest, + ListStakesResponse, +} from '../gen/coinbase/staking/rewards/v1/stake.pb'; -import { EthereumKiln } from "./protocols/ethereum_kiln_staking"; +import { Ethereum } from './protocols/ethereum-kiln-staking'; +import { Solana } from './protocols/solana-staking'; -const DEFAULT_URL = "https://api.developer.coinbase.com/staking"; +const DEFAULT_URL = 'https://api.developer.coinbase.com/staking'; -export class StakingServiceClient { +export class StakingClient { readonly baseURL: string; - readonly EthereumKiln: EthereumKiln; + readonly Ethereum: Ethereum; + readonly Solana: Solana; constructor(baseURL?: string) { if (baseURL) { @@ -46,16 +56,18 @@ export class StakingServiceClient { this.baseURL = DEFAULT_URL; } - this.EthereumKiln = new EthereumKiln(this); + this.Ethereum = new Ethereum(this); + this.Solana = new Solana(this); } // List protocols supported by Staking API async listProtocols(): Promise { - const path: string = "/api/v1alpha1/protocols"; - const method: string = "GET"; + const path: string = '/v1/protocols'; + const method: string = 'GET'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); const req: ListProtocolsRequest = {}; @@ -65,11 +77,12 @@ export class StakingServiceClient { // 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"; + const path: string = `/v1/${parent}/networks`; + const method: string = 'GET'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); const req: ListNetworksRequest = { parent: parent, @@ -84,11 +97,12 @@ export class StakingServiceClient { network: string, ): Promise { const parent: string = `protocols/${protocol}/networks/${network}`; - const path: string = `/api/v1alpha1/${parent}/actions`; - const method: string = "GET"; + const path: string = `/v1/${parent}/actions`; + const method: string = 'GET'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); const req: ListActionsRequest = { parent: parent, @@ -98,32 +112,34 @@ export class StakingServiceClient { } // 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 + // Use the protocol-specific helper functions like Ethereum.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"; + const path: string = '/v1/viewStakingContext:view'; + const method: string = 'GET'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, 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. + // Use the protocol-specific helper functions like Ethereum.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"; + const path: string = `/v1/${parent}/workflows`; + const method: string = 'POST'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); return StakingService.CreateWorkflow(req, initReq); } @@ -132,11 +148,12 @@ export class StakingServiceClient { 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"; + const path: string = `/v1/${name}`; + const method: string = 'GET'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); const req: GetWorkflowRequest = { name: name, @@ -154,11 +171,12 @@ export class StakingServiceClient { ): Promise { const parent: string = `projects/${projectId}`; const name: string = `${parent}/workflows/${workflowId}`; - const path: string = `/api/v1alpha1/${name}/step`; - const method: string = "POST"; + const path: string = `/v1/${name}/step`; + const method: string = 'POST'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); const req: PerformWorkflowStepRequest = { name: name, @@ -169,40 +187,19 @@ export class StakingServiceClient { 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 = "", + filter: string = '', ): Promise { const parent: string = `projects/${project}`; - const path: string = `/api/v1alpha1/${parent}/workflows`; - const method: string = "GET"; + const path: string = `/v1/${parent}/workflows`; + const method: string = 'GET'; + const url: string = this.baseURL + '/orchestration'; // Generate the JWT token and get the auth details as a initReq object. - const initReq = await getAuthDetails(this.baseURL, path, method); + const initReq = await getAuthDetails(url, path, method); const req: ListWorkflowsRequest = { parent: parent, @@ -212,14 +209,44 @@ export class StakingServiceClient { return StakingService.ListWorkflows(req, initReq); } + + // List onchain rewards of an address for a specific protocol, with optional filters for time range, aggregation period, and more. + async listRewards( + protocol: string, + req: ListRewardsRequest, + ): Promise { + const parent: string = `protocols/${protocol}`; + const path: string = `/v1/${parent}/rewards`; + const method: string = 'GET'; + const url: string = this.baseURL + '/rewards'; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(url, path, method); + + return RewardService.ListRewards(req, initReq); + } + + // List staking activities for a given protocol. + async listStakes( + protocol: string, + req: ListStakesRequest, + ): Promise { + const parent: string = `protocols/${protocol}`; + const path: string = `/v1/${parent}/stakes`; + const method: string = 'GET'; + const url: string = this.baseURL + '/rewards'; + + // Generate the JWT token and get the auth details as a initReq object. + const initReq = await getAuthDetails(url, path, method); + + return RewardService.ListStakes(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 + workflow.state === WorkflowState.STATE_FAILED ); } @@ -233,28 +260,28 @@ export function workflowWaitingForExternalBroadcast( 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 } { +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 } { +export function isWaitStepOutput( + step: WorkflowStep, +): step is WorkflowStep & { waitStepOutput: WaitStepOutput } { return (step as WorkflowStep).waitStepOutput !== undefined; } async function getAuthDetails( - baseURL: string, + url: string, path: string, method: string, ): Promise { // Generate the JWT token - const token = await buildJWT(baseURL + path, method); + const token = await buildJWT(url + path, method); return { - pathPrefix: baseURL, + pathPrefix: url, headers: { Authorization: `Bearer ${token}`, }, diff --git a/src/examples/public/example.ts b/src/examples/public/example.ts deleted file mode 100644 index 2497318..0000000 --- a/src/examples/public/example.ts +++ /dev/null @@ -1,15 +0,0 @@ -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/gen/coinbase/staking/v1alpha1/action.pb.ts b/src/gen/coinbase/staking/orchestration/v1/action.pb.ts similarity index 100% rename from src/gen/coinbase/staking/v1alpha1/action.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/action.pb.ts diff --git a/src/gen/coinbase/staking/orchestration/v1/api.pb.ts b/src/gen/coinbase/staking/orchestration/v1/api.pb.ts new file mode 100644 index 0000000..5f524a3 --- /dev/null +++ b/src/gen/coinbase/staking/orchestration/v1/api.pb.ts @@ -0,0 +1,42 @@ +/* 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 CoinbaseStakingOrchestrationV1Action from "./action.pb" +import * as CoinbaseStakingOrchestrationV1Network from "./network.pb" +import * as CoinbaseStakingOrchestrationV1Protocol from "./protocol.pb" +import * as CoinbaseStakingOrchestrationV1Staking_context from "./staking_context.pb" +import * as CoinbaseStakingOrchestrationV1Staking_target from "./staking_target.pb" +import * as CoinbaseStakingOrchestrationV1Workflow from "./workflow.pb" +export class StakingService { + static ListProtocols(req: CoinbaseStakingOrchestrationV1Protocol.ListProtocolsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/protocols?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) + } + static ListNetworks(req: CoinbaseStakingOrchestrationV1Network.ListNetworksRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/networks?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static ListStakingTargets(req: CoinbaseStakingOrchestrationV1Staking_target.ListStakingTargetsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/stakingTargets?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static ListActions(req: CoinbaseStakingOrchestrationV1Action.ListActionsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/actions?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static CreateWorkflow(req: CoinbaseStakingOrchestrationV1Workflow.CreateWorkflowRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/workflows`, {...initReq, method: "POST", body: JSON.stringify(req["workflow"], fm.replacer)}) + } + static GetWorkflow(req: CoinbaseStakingOrchestrationV1Workflow.GetWorkflowRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["name"]}?${fm.renderURLSearchParams(req, ["name"])}`, {...initReq, method: "GET"}) + } + static ListWorkflows(req: CoinbaseStakingOrchestrationV1Workflow.ListWorkflowsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/workflows?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static PerformWorkflowStep(req: CoinbaseStakingOrchestrationV1Workflow.PerformWorkflowStepRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["name"]}/step`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)}) + } + static ViewStakingContext(req: CoinbaseStakingOrchestrationV1Staking_context.ViewStakingContextRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/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/orchestration/v1/common.pb.ts similarity index 100% rename from src/gen/coinbase/staking/v1alpha1/common.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/common.pb.ts diff --git a/src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts b/src/gen/coinbase/staking/orchestration/v1/ethereum_kiln.pb.ts similarity index 65% rename from src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/ethereum_kiln.pb.ts index bcb0861..7ef1123 100644 --- a/src/gen/coinbase/staking/v1alpha1/ethereum_kiln.pb.ts +++ b/src/gen/coinbase/staking/orchestration/v1/ethereum_kiln.pb.ts @@ -4,7 +4,7 @@ * This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY */ -import * as CoinbaseStakingV1alpha1Common from "./common.pb" +import * as CoinbaseStakingOrchestrationV1Common from "./common.pb" type Absent = { [k in Exclude]?: undefined }; type OneOf = @@ -17,13 +17,13 @@ type OneOf = export type EthereumKilnStakeParameters = { stakerAddress?: string integratorContractAddress?: string - amount?: CoinbaseStakingV1alpha1Common.Amount + amount?: CoinbaseStakingOrchestrationV1Common.Amount } export type EthereumKilnUnstakeParameters = { stakerAddress?: string integratorContractAddress?: string - amount?: CoinbaseStakingV1alpha1Common.Amount + amount?: CoinbaseStakingOrchestrationV1Common.Amount } export type EthereumKilnClaimStakeParameters = { @@ -43,10 +43,10 @@ export type EthereumKilnStakingContextParameters = { } export type EthereumKilnStakingContextDetails = { - ethereumBalance?: CoinbaseStakingV1alpha1Common.Amount - integratorShareBalance?: CoinbaseStakingV1alpha1Common.Amount - integratorShareUnderlyingBalance?: CoinbaseStakingV1alpha1Common.Amount - totalExitableEth?: CoinbaseStakingV1alpha1Common.Amount - totalSharesPendingExit?: CoinbaseStakingV1alpha1Common.Amount - fulfillableShareCount?: CoinbaseStakingV1alpha1Common.Amount + ethereumBalance?: CoinbaseStakingOrchestrationV1Common.Amount + integratorShareBalance?: CoinbaseStakingOrchestrationV1Common.Amount + integratorShareUnderlyingBalance?: CoinbaseStakingOrchestrationV1Common.Amount + totalExitableEth?: CoinbaseStakingOrchestrationV1Common.Amount + totalSharesPendingExit?: CoinbaseStakingOrchestrationV1Common.Amount + fulfillableShareCount?: CoinbaseStakingOrchestrationV1Common.Amount } \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/network.pb.ts b/src/gen/coinbase/staking/orchestration/v1/network.pb.ts similarity index 92% rename from src/gen/coinbase/staking/v1alpha1/network.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/network.pb.ts index be49cf0..cee02ce 100644 --- a/src/gen/coinbase/staking/v1alpha1/network.pb.ts +++ b/src/gen/coinbase/staking/orchestration/v1/network.pb.ts @@ -5,7 +5,6 @@ */ export type Network = { name?: string - isMainnet?: boolean } export type ListNetworksRequest = { diff --git a/src/gen/coinbase/staking/v1alpha1/protocol.pb.ts b/src/gen/coinbase/staking/orchestration/v1/protocol.pb.ts similarity index 100% rename from src/gen/coinbase/staking/v1alpha1/protocol.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/protocol.pb.ts diff --git a/src/gen/coinbase/staking/orchestration/v1/solana.pb.ts b/src/gen/coinbase/staking/orchestration/v1/solana.pb.ts new file mode 100644 index 0000000..ca9d084 --- /dev/null +++ b/src/gen/coinbase/staking/orchestration/v1/solana.pb.ts @@ -0,0 +1,75 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ + +import * as CoinbaseStakingOrchestrationV1Common 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 enum StakeAccountBalanceState { + BALANCE_STATE_UNSPECIFIED = "BALANCE_STATE_UNSPECIFIED", + BALANCE_STATE_INACTIVE = "BALANCE_STATE_INACTIVE", + BALANCE_STATE_ACTIVATING = "BALANCE_STATE_ACTIVATING", + BALANCE_STATE_ACTIVE = "BALANCE_STATE_ACTIVE", + BALANCE_STATE_DEACTIVATING = "BALANCE_STATE_DEACTIVATING", +} + +export type PriorityFee = { + computeUnitLimit?: string + unitPrice?: string +} + +export type SolanaStakeParameters = { + walletAddress?: string + validatorAddress?: string + amount?: CoinbaseStakingOrchestrationV1Common.Amount + priorityFee?: PriorityFee +} + +export type SolanaUnstakeParameters = { + walletAddress?: string + stakeAccountAddress?: string + amount?: CoinbaseStakingOrchestrationV1Common.Amount + priorityFee?: PriorityFee +} + +export type SolanaClaimStakeParameters = { + walletAddress?: string + stakeAccountAddress?: string + priorityFee?: PriorityFee +} + +export type SolanaStakingContextParameters = { +} + +export type SolanaStakingContextDetails = { + balance?: CoinbaseStakingOrchestrationV1Common.Amount + currentEpoch?: string + epochCompletionPercentage?: string + stakeAccounts?: StakeAccount[] +} + +export type StakeAccount = { + address?: string + bondedStake?: CoinbaseStakingOrchestrationV1Common.Amount + rentReserve?: CoinbaseStakingOrchestrationV1Common.Amount + balance?: CoinbaseStakingOrchestrationV1Common.Amount + balanceState?: StakeAccountBalanceState + validator?: string +} + + +type BaseSolanaStakingParameters = { +} + +export type SolanaStakingParameters = BaseSolanaStakingParameters + & OneOf<{ stakeParameters: SolanaStakeParameters; unstakeParameters: SolanaUnstakeParameters; claimStakeParameters: SolanaClaimStakeParameters }> \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts b/src/gen/coinbase/staking/orchestration/v1/staking_context.pb.ts similarity index 57% rename from src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/staking_context.pb.ts index ebc8eb6..6242db3 100644 --- a/src/gen/coinbase/staking/v1alpha1/staking_context.pb.ts +++ b/src/gen/coinbase/staking/orchestration/v1/staking_context.pb.ts @@ -4,7 +4,8 @@ * This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY */ -import * as CoinbaseStakingV1alpha1Ethereum_kiln from "./ethereum_kiln.pb" +import * as CoinbaseStakingOrchestrationV1Ethereum_kiln from "./ethereum_kiln.pb" +import * as CoinbaseStakingOrchestrationV1Solana from "./solana.pb" type Absent = { [k in Exclude]?: undefined }; type OneOf = @@ -21,7 +22,7 @@ type BaseViewStakingContextRequest = { } export type ViewStakingContextRequest = BaseViewStakingContextRequest - & OneOf<{ ethereumKilnStakingContextParameters: CoinbaseStakingV1alpha1Ethereum_kiln.EthereumKilnStakingContextParameters }> + & OneOf<{ ethereumKilnStakingContextParameters: CoinbaseStakingOrchestrationV1Ethereum_kiln.EthereumKilnStakingContextParameters; solanaStakingContextParameters: CoinbaseStakingOrchestrationV1Solana.SolanaStakingContextParameters }> type BaseViewStakingContextResponse = { @@ -29,4 +30,4 @@ type BaseViewStakingContextResponse = { } export type ViewStakingContextResponse = BaseViewStakingContextResponse - & OneOf<{ ethereumKilnStakingContextDetails: CoinbaseStakingV1alpha1Ethereum_kiln.EthereumKilnStakingContextDetails }> \ No newline at end of file + & OneOf<{ ethereumKilnStakingContextDetails: CoinbaseStakingOrchestrationV1Ethereum_kiln.EthereumKilnStakingContextDetails; solanaStakingContextDetails: CoinbaseStakingOrchestrationV1Solana.SolanaStakingContextDetails }> \ No newline at end of file diff --git a/src/gen/coinbase/staking/v1alpha1/staking_target.pb.ts b/src/gen/coinbase/staking/orchestration/v1/staking_target.pb.ts similarity index 100% rename from src/gen/coinbase/staking/v1alpha1/staking_target.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/staking_target.pb.ts diff --git a/src/gen/coinbase/staking/v1alpha1/workflow.pb.ts b/src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts similarity index 74% rename from src/gen/coinbase/staking/v1alpha1/workflow.pb.ts rename to src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts index cc0a898..cc6549e 100644 --- a/src/gen/coinbase/staking/v1alpha1/workflow.pb.ts +++ b/src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts @@ -4,10 +4,9 @@ * 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" +import * as GoogleProtobufTimestamp from "../../../../google/protobuf/timestamp.pb" +import * as CoinbaseStakingOrchestrationV1Ethereum_kiln from "./ethereum_kiln.pb" +import * as CoinbaseStakingOrchestrationV1Solana from "./solana.pb" type Absent = { [k in Exclude]?: undefined }; type OneOf = @@ -30,11 +29,6 @@ export enum TxStepOutputState { 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", } @@ -59,10 +53,6 @@ export enum WorkflowState { 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", } @@ -104,7 +94,7 @@ type BaseWorkflow = { } export type Workflow = BaseWorkflow - & OneOf<{ polygonStakingParameters: CoinbaseStakingV1alpha1Polygon.PolygonStakingParameters; solanaStakingParameters: CoinbaseStakingV1alpha1Solana.SolanaStakingParameters; ethereumKilnStakingParameters: CoinbaseStakingV1alpha1Ethereum_kiln.EthereumKilnStakingParameters }> + & OneOf<{ solanaStakingParameters: CoinbaseStakingOrchestrationV1Solana.SolanaStakingParameters; ethereumKilnStakingParameters: CoinbaseStakingOrchestrationV1Ethereum_kiln.EthereumKilnStakingParameters }> export type CreateWorkflowRequest = { parent?: string @@ -131,9 +121,4 @@ 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/coinbase/staking/rewards/v1/common.pb.ts b/src/gen/coinbase/staking/rewards/v1/common.pb.ts new file mode 100644 index 0000000..64c5b9b --- /dev/null +++ b/src/gen/coinbase/staking/rewards/v1/common.pb.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type AssetAmount = { + amount?: string + exp?: string + ticker?: string + rawNumeric?: string +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/rewards/v1/protocol.pb.ts b/src/gen/coinbase/staking/rewards/v1/protocol.pb.ts new file mode 100644 index 0000000..2730aab --- /dev/null +++ b/src/gen/coinbase/staking/rewards/v1/protocol.pb.ts @@ -0,0 +1,8 @@ +/* eslint-disable */ +// @ts-nocheck +/* +* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY +*/ +export type Protocol = { + name?: string +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/rewards/v1/reward.pb.ts b/src/gen/coinbase/staking/rewards/v1/reward.pb.ts new file mode 100644 index 0000000..8f6d426 --- /dev/null +++ b/src/gen/coinbase/staking/rewards/v1/reward.pb.ts @@ -0,0 +1,62 @@ +/* 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 CoinbaseStakingRewardsV1Common from "./common.pb" +import * as CoinbaseStakingRewardsV1Stake from "./stake.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 AggregationUnit { + AGGREGATION_UNIT_UNSPECIFIED = "AGGREGATION_UNIT_UNSPECIFIED", + EPOCH = "EPOCH", + DAY = "DAY", +} + +export enum USDValueSource { + SOURCE_UNSPECIFIED = "SOURCE_UNSPECIFIED", + COINBASE_EXCHANGE = "COINBASE_EXCHANGE", +} + + +type BaseReward = { + address?: string + aggregationUnit?: AggregationUnit + periodStartTime?: GoogleProtobufTimestamp.Timestamp + periodEndTime?: GoogleProtobufTimestamp.Timestamp + totalEarnedNativeUnit?: CoinbaseStakingRewardsV1Common.AssetAmount + totalEarnedUsd?: USDValue[] + endingBalance?: CoinbaseStakingRewardsV1Stake.Stake + protocol?: string +} + +export type Reward = BaseReward + & OneOf<{ epoch: string; date: string }> + +export type USDValue = { + source?: USDValueSource + conversionTime?: GoogleProtobufTimestamp.Timestamp + amount?: CoinbaseStakingRewardsV1Common.AssetAmount +} + +export type ListRewardsRequest = { + parent?: string + pageSize?: number + pageToken?: string + filter?: string +} + +export type ListRewardsResponse = { + rewards?: Reward[] + nextPageToken?: string +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/rewards/v1/reward_service.pb.ts b/src/gen/coinbase/staking/rewards/v1/reward_service.pb.ts new file mode 100644 index 0000000..f58b880 --- /dev/null +++ b/src/gen/coinbase/staking/rewards/v1/reward_service.pb.ts @@ -0,0 +1,20 @@ +/* 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 CoinbaseStakingRewardsV1Reward from "./reward.pb" +import * as CoinbaseStakingRewardsV1Stake from "./stake.pb" +export class RewardService { + static ListRewards(req: CoinbaseStakingRewardsV1Reward.ListRewardsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/rewards?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } + static GetStake(req: CoinbaseStakingRewardsV1Stake.GetStakeRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["nameprotocolsstakes"]}?${fm.renderURLSearchParams(req, ["nameprotocolsstakes"])}`, {...initReq, method: "GET"}) + } + static ListStakes(req: CoinbaseStakingRewardsV1Stake.ListStakesRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/${req["parent"]}/stakes?${fm.renderURLSearchParams(req, ["parent"])}`, {...initReq, method: "GET"}) + } +} \ No newline at end of file diff --git a/src/gen/coinbase/staking/rewards/v1/stake.pb.ts b/src/gen/coinbase/staking/rewards/v1/stake.pb.ts new file mode 100644 index 0000000..de6c259 --- /dev/null +++ b/src/gen/coinbase/staking/rewards/v1/stake.pb.ts @@ -0,0 +1,75 @@ +/* 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 CoinbaseStakingRewardsV1Common 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 enum ParticipantType { + PARTICIPANT_TYPE_UNSPECIFIED = "PARTICIPANT_TYPE_UNSPECIFIED", + DELEGATOR = "DELEGATOR", + VALIDATOR = "VALIDATOR", +} + +export enum RewardRateCalculationMethods { + CALCULATION_METHODS_UNSPECIFIED = "CALCULATION_METHODS_UNSPECIFIED", + SOLO_STAKER = "SOLO_STAKER", + POOLED_STAKER = "POOLED_STAKER", + EPOCH_AUTO_COMPOUNDING = "EPOCH_AUTO_COMPOUNDING", + NO_AUTO_COMPOUNDING = "NO_AUTO_COMPOUNDING", +} + +export type StakeDelegation = { + address?: string + amount?: CoinbaseStakingRewardsV1Common.AssetAmount + commissionRate?: string +} + + +type BaseStake = { + address?: string + evaluationTime?: GoogleProtobufTimestamp.Timestamp + bondedStake?: CoinbaseStakingRewardsV1Common.AssetAmount + totalDelegationReceived?: CoinbaseStakingRewardsV1Common.AssetAmount + rewardRateCalculations?: RewardRate[] + participantType?: ParticipantType + protocol?: string + unbondedBalance?: CoinbaseStakingRewardsV1Common.AssetAmount +} + +export type Stake = BaseStake + & OneOf<{ delegationsReceived: StakeDelegation }> + & OneOf<{ delegationsGiven: StakeDelegation }> + +export type RewardRate = { + percentage?: string + calculatedTime?: GoogleProtobufTimestamp.Timestamp + calculationMethod?: RewardRateCalculationMethods +} + +export type ListStakesRequest = { + parent?: string + pageSize?: number + pageToken?: string + filter?: string +} + +export type ListStakesResponse = { + stakes?: Stake[] + nextPageToken?: string +} + +export type GetStakeRequest = { + name?: string +} \ 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 deleted file mode 100644 index 7eafe2d..0000000 --- a/src/gen/coinbase/staking/v1alpha1/api.pb.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* 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/polygon.pb.ts b/src/gen/coinbase/staking/v1alpha1/polygon.pb.ts deleted file mode 100644 index 41ad9ab..0000000 --- a/src/gen/coinbase/staking/v1alpha1/polygon.pb.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* 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/solana.pb.ts b/src/gen/coinbase/staking/v1alpha1/solana.pb.ts deleted file mode 100644 index 41f0a26..0000000 --- a/src/gen/coinbase/staking/v1alpha1/solana.pb.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* 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/index.ts b/src/index.ts index 091e243..870ba28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -import * as Utils from "./utils/date"; +import * as Utils from './utils/date'; export { Utils }; -export { StakingServiceClient } from "./client/staking-service-client"; +export { StakingClient } from './client/staking-client'; diff --git a/src/signers/ethereum_signer.ts b/src/signers/ethereum-signer.ts similarity index 59% rename from src/signers/ethereum_signer.ts rename to src/signers/ethereum-signer.ts index 626bbca..e834378 100644 --- a/src/signers/ethereum_signer.ts +++ b/src/signers/ethereum-signer.ts @@ -1,6 +1,6 @@ -import { TransactionFactory } from "@ethereumjs/tx"; -import { bytesToHex } from "@ethereumjs/util"; -import { TxSigner } from "./txsigner"; +import { TransactionFactory } from '@ethereumjs/tx'; +import { bytesToHex } from '@ethereumjs/util'; +import { TxSigner } from './txsigner'; export class EthereumTransactionSigner implements TxSigner { async signTransaction( @@ -8,17 +8,17 @@ export class EthereumTransactionSigner implements TxSigner { unsignedTx: string, ): Promise { let transaction = TransactionFactory.fromSerializedData( - Buffer.from(unsignedTx, "hex"), + Buffer.from(unsignedTx, 'hex'), ); - const decodedPrivateKey = Buffer.from(privateKey, "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!"); + throw new Error('Produced an invalid signature!'); } return bytesToHex(signedTx.serialize()); diff --git a/src/signers/index.ts b/src/signers/index.ts index 8dc7b48..b7949a4 100644 --- a/src/signers/index.ts +++ b/src/signers/index.ts @@ -1,2 +1,2 @@ -export * from "./ethereum_signer"; -export * from "./txsigner"; +export * from './ethereum-signer'; +export * from './txsigner'; diff --git a/src/signers/txsigner.ts b/src/signers/txsigner.ts index 16f4200..76938c4 100644 --- a/src/signers/txsigner.ts +++ b/src/signers/txsigner.ts @@ -1,4 +1,4 @@ -import { EthereumTransactionSigner } from "./ethereum_signer"; +import { EthereumTransactionSigner } from './ethereum-signer'; export interface TxSigner { // eslint-disable-next-line no-unused-vars @@ -8,11 +8,11 @@ export interface TxSigner { export class TxSignerFactory { static getSigner(protocol: string): TxSigner { switch (protocol) { - case "ethereum": + case 'ethereum': return new EthereumTransactionSigner(); // other cases for additional protocols default: - throw new Error("Unsupported protocol"); + throw new Error('Unsupported protocol'); } } } diff --git a/tsconfig.json b/tsconfig.json index f49258b..1dc7e70 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,32 @@ { "compilerOptions": { - "target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "module": "CommonJS", /* Specify what module code is generated. */ + "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. */ + "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. */ + "moduleResolution": "node", + "noImplicitAny": true, + "strictNullChecks": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "types": [ + "node" + ] }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules", "openapi"] + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "openapi" + ] }