From fe7d83e2fa97b2280d1829163fbe055bf2801a71 Mon Sep 17 00:00:00 2001 From: Louis-Vincent Date: Wed, 20 Nov 2024 15:00:33 -0500 Subject: [PATCH 1/7] added fumarole proto --- yellowstone-grpc-proto/build.rs | 4 +- yellowstone-grpc-proto/proto/fumarole.proto | 75 +++++++++++++++++++++ yellowstone-grpc-proto/src/lib.rs | 4 ++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 yellowstone-grpc-proto/proto/fumarole.proto diff --git a/yellowstone-grpc-proto/build.rs b/yellowstone-grpc-proto/build.rs index 9df65193..65160c8a 100644 --- a/yellowstone-grpc-proto/build.rs +++ b/yellowstone-grpc-proto/build.rs @@ -2,9 +2,8 @@ use tonic_build::manual::{Builder, Method, Service}; fn main() -> anyhow::Result<()> { std::env::set_var("PROTOC", protobuf_src::protoc()); - // build protos - tonic_build::compile_protos("proto/geyser.proto")?; + tonic_build::compile_protos("proto/fumarole.proto")?; // build with accepting our custom struct let geyser_service = Service::builder() @@ -78,6 +77,7 @@ fn main() -> anyhow::Result<()> { .build(), ) .build(); + Builder::new() .build_client(false) .compile(&[geyser_service]); diff --git a/yellowstone-grpc-proto/proto/fumarole.proto b/yellowstone-grpc-proto/proto/fumarole.proto new file mode 100644 index 00000000..5a28ef6d --- /dev/null +++ b/yellowstone-grpc-proto/proto/fumarole.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +option go_package = "github.com/rpcpool/solana-geyser-grpc/golang/proto"; + +import public "geyser.proto"; + +package fumarole; + +service Fumarole { + rpc CreateStaticConsumerGroup(CreateStaticConsumerGroupRequest) returns (CreateStaticConsumerGroupResponse) {} + rpc Subscribe(stream SubscribeRequest) returns (stream geyser.SubscribeUpdate) {} + rpc GetSlotLagInfo(GetSlotLagInfoRequest) returns (GetSlotLagInfoResponse) {} +} + +message GetSlotLagInfoRequest { + string consumer_group_label = 1; +} + +message GetSlotLagInfoResponse { + uint64 max_slot_seen = 1; + uint64 global_max_slot = 2; +} + +message SubscribeRequest { + string consumer_group_label = 1; + optional uint32 consumer_id = 2; + optional AccountUpdateFilter accounts = 3; + optional TransactionFilter transactions = 4; +} + +message CreateStaticConsumerGroupResponse { + string group_id = 1; +} + +enum InitialOffsetPolicy { + EARLIEST = 0; + LATEST = 1; + SLOT = 2; +} + +enum EventSubscriptionPolicy { + ACCOUNT_UPDATE_ONLY = 0; + TRANSACTION_ONLY = 1; + BOTH = 2; +} + +message CreateStaticConsumerGroupRequest { + string consumer_group_label = 1; + optional uint32 member_count = 2; + InitialOffsetPolicy initial_offset_policy = 3; + geyser.CommitmentLevel commitment_level = 4; + EventSubscriptionPolicy event_subscription_policy = 5; + optional int64 at_slot = 6; +} + + +/// The AccountUpdateFilter message defines filters for account update events. It includes the following fields: +/// +/// pubkeys (1) +/// A repeated field of bytes representing public keys. Events matching any of these public keys will be included in the filtered results. +/// +/// owners (2) +/// A repeated field of bytes representing account owners. Events matching any of these account owners will be included in the filtered results. +message AccountUpdateFilter { + repeated string account = 1; + repeated string owner = 2; +} + +/// The TransactionFilter message specifies filters for transaction events. It contains the following field: + +/// account_keys (1) +/// A repeated field of bytes representing account keys. Events associated with any of these account keys will be included in the filtered results. +message TransactionFilter { + repeated string account_keys = 1; +} \ No newline at end of file diff --git a/yellowstone-grpc-proto/src/lib.rs b/yellowstone-grpc-proto/src/lib.rs index f3bb8cea..9c4a56c4 100644 --- a/yellowstone-grpc-proto/src/lib.rs +++ b/yellowstone-grpc-proto/src/lib.rs @@ -17,6 +17,10 @@ pub mod solana { } } +pub mod fumarole { + tonic::include_proto!("fumarole"); +} + pub mod prelude { pub use super::{geyser::*, solana::storage::confirmed_block::*}; } From d8ec78295e030e2a990d61366cbdbaa65dee8f31 Mon Sep 17 00:00:00 2001 From: Wilfred Almeida Date: Thu, 12 Dec 2024 19:01:48 +0530 Subject: [PATCH 2/7] wip Signed-off-by: Wilfred Almeida --- examples/typescript/README.md | 17 +- examples/typescript/package.json | 7 +- examples/typescript/pnpm-lock.yaml | 291 +++++++++++++++++++++++++++++ examples/typescript/src/client.ts | 191 +++++++++++++++++++ 4 files changed, 500 insertions(+), 6 deletions(-) create mode 100644 examples/typescript/pnpm-lock.yaml diff --git a/examples/typescript/README.md b/examples/typescript/README.md index fd69b1e9..639c86ed 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -11,8 +11,8 @@ This is a sample client for Solana geyser gRPC written in TypeScript. This can be used in the following way: ```shell -npm start -- --endpoint https://api.rpcpool.com \ - --x-token \ +npm start -- --endpoint https://url \ + --x-token token \ subscribe \ --accounts --accounts-account SysvarC1ock11111111111111111111111111111111 ``` @@ -72,7 +72,7 @@ response: 1 ### unary GetLatestBlockhash ```shell -npm start -- -e="https://api.rpcpool.com" \ +npm start -- -e="http://fumarole-fumarole-server.service.consul:26847" \ --x-token "" \ get-latest-blockhash ``` @@ -132,3 +132,14 @@ npm start -- -e="https://api.rpcpool.com" \ ```text response: { version: "{\"version\":\"0.7.0+solana.1.15.2\",\"proto\":\"1.2.0+solana.1.15.2\",\"solana\":\"1.15.2\",\"git\":\"e03a47c-modified\",\"rustc\":\"1.68.0-nightly\",\"buildts\":\"2023-05-27T08:20:15.440278Z\"}" } ``` + +## Fumarole + +### Create Consumer Group +pnpm start -- -e="http://traefik.service.ams1.consul:28080/" \ +create-consumer-group + +### Subscribe +pnpm start -- -e="http://traefik.service.ams1.consul:28080/" \ +fumarole-subscribe \ +--accounts --accounts-account SysvarC1ock11111111111111111111111111111111 \ No newline at end of file diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 53252f82..d1125a7b 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -7,13 +7,14 @@ "description": "Yellowstone gRPC Geyser TypeScript example", "homepage": "https://triton.one", "dependencies": { - "yargs": "^17.6.2", - "@triton-one/yellowstone-grpc": "file:../../yellowstone-grpc-client-nodejs" + "@grpc/grpc-js": "^1.12.4", + "@triton-one/yellowstone-grpc": "file:../../yellowstone-grpc-client-nodejs", + "yargs": "^17.6.2" }, "scripts": { "build": "tsc -p .", "fmt": "prettier -w .", - "start": "npm run build && node dist/client.js" + "start": "pnpm run build && node dist/client.js" }, "devDependencies": { "prettier": "^2.8.3", diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml new file mode 100644 index 00000000..f2619e7d --- /dev/null +++ b/examples/typescript/pnpm-lock.yaml @@ -0,0 +1,291 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@grpc/grpc-js': + specifier: ^1.12.4 + version: 1.12.4 + '@triton-one/yellowstone-grpc': + specifier: file:../../yellowstone-grpc-client-nodejs + version: file:../../yellowstone-grpc-client-nodejs + yargs: + specifier: ^17.6.2 + version: 17.7.2 + devDependencies: + prettier: + specifier: ^2.8.3 + version: 2.8.8 + typescript: + specifier: ^4.9.5 + version: 4.9.5 + +packages: + + '@grpc/grpc-js@1.12.4': + resolution: {integrity: sha512-NBhrxEWnFh0FxeA0d//YP95lRFsSx2TNLEUQg4/W+5f/BMxcCjgOOIT24iD+ZB/tZw057j44DaIxja7w4XMrhg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.13': + resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} + engines: {node: '>=6'} + hasBin: true + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@triton-one/yellowstone-grpc@file:../../yellowstone-grpc-client-nodejs': + resolution: {directory: ../../yellowstone-grpc-client-nodejs, type: directory} + + '@types/node@22.10.2': + resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + '@grpc/grpc-js@1.12.4': + dependencies: + '@grpc/proto-loader': 0.7.13 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.13': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.4.0 + yargs: 17.7.2 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@triton-one/yellowstone-grpc@file:../../yellowstone-grpc-client-nodejs': + dependencies: + '@grpc/grpc-js': 1.12.4 + + '@types/node@22.10.2': + dependencies: + undici-types: 6.20.0 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + emoji-regex@8.0.0: {} + + escalade@3.2.0: {} + + get-caller-file@2.0.5: {} + + is-fullwidth-code-point@3.0.0: {} + + lodash.camelcase@4.3.0: {} + + long@5.2.3: {} + + prettier@2.8.8: {} + + protobufjs@7.4.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.10.2 + long: 5.2.3 + + require-directory@2.1.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + typescript@4.9.5: {} + + undici-types@6.20.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 diff --git a/examples/typescript/src/client.ts b/examples/typescript/src/client.ts index 9d3e22dd..f57eb0e2 100644 --- a/examples/typescript/src/client.ts +++ b/examples/typescript/src/client.ts @@ -1,10 +1,14 @@ import yargs from "yargs"; import Client, { CommitmentLevel, + FumaroleSDKClient, + FumaroleSubscribeRequest, SubscribeRequest, SubscribeRequestFilterAccountsFilter, SubscribeRequestFilterAccountsFilterLamports, } from "@triton-one/yellowstone-grpc"; +import { EventSubscriptionPolicy, FumaroleClient, InitialOffsetPolicy } from "@triton-one/yellowstone-grpc/dist/grpc/fumarole"; +import { Metadata } from "@grpc/grpc-js"; async function main() { const args = parseCommandLineArgs(); @@ -14,6 +18,11 @@ async function main() { "grpc.max_receive_message_length": 64 * 1024 * 1024, // 64MiB }); + const fumaroleSubscriptionId = crypto.randomUUID() + const fumaroleClient = new FumaroleSDKClient(args.endpoint, args.xToken, { + "grpc.max_receive_message_length": 64 * 1024 * 1024, // 64MiB, + }, fumaroleSubscriptionId); + const commitment = parseCommitmentLevel(args.commitment); // Execute a requested command @@ -46,6 +55,51 @@ async function main() { await subscribeCommand(client, args); break; + case "fumarole-subscribe": + + const metadata2: Metadata = new Metadata() + + const label2 = generateRandomString(6) + + console.log(`subscription id ${fumaroleSubscriptionId}`); + console.log(`label ${label2}`); + + metadata2.add("x-subscription-id", fumaroleSubscriptionId) + + console.log(await fumaroleClient.createConsumerGroup({ + commitmentLevel: CommitmentLevel.CONFIRMED, + consumerGroupLabel: label2, + eventSubscriptionPolicy: EventSubscriptionPolicy.BOTH, + initialOffsetPolicy: InitialOffsetPolicy.EARLIEST, + memberCount: 2 + }, metadata2)) + + + args.consumerGroupLabel = label2 + + await sleep(10000) + + await fumaroleSubscribeCommand(fumaroleClient, args) + break; + + case "create-consumer-group": + const metadata: Metadata = new Metadata() + + const label = generateRandomString(6) + + console.log(`subscription id ${fumaroleSubscriptionId}`); + console.log(`label ${label}`); + + metadata.add("x-subscription-id", fumaroleSubscriptionId) + + console.log(await fumaroleClient.createConsumerGroup({ + commitmentLevel: CommitmentLevel.CONFIRMED, + consumerGroupLabel: label, + eventSubscriptionPolicy: EventSubscriptionPolicy.BOTH, + initialOffsetPolicy: InitialOffsetPolicy.EARLIEST + }, metadata)) + break; + default: console.error( `Unknown command: ${args["_"]}. Use "--help" for a list of supported commands.` @@ -63,6 +117,68 @@ function parseCommitmentLevel(commitment: string | undefined) { return CommitmentLevel[typedCommitment]; } + +async function fumaroleSubscribeCommand(client: FumaroleSDKClient, args) { + + // Subscribe for events + const stream = await client.subscribe(); + + // Create `error` / `end` handler + const streamClosed = new Promise((resolve, reject) => { + stream.on("error", (error) => { + reject(error); + stream.end(); + }); + stream.on("end", () => { + resolve(); + }); + stream.on("close", () => { + resolve(); + }); + }); + + // Handle updates + stream.on("data", (data) => { + console.log("data", data); + }); + + // Create subscribe request based on provided arguments. + const request: FumaroleSubscribeRequest = { + transactions: { + accountKeys: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"] + }, + consumerGroupLabel: args.consumerGroupLabel, + consumerId: 1 + }; + // if (args.accounts) { + // if (args.accountsAccount) { + // request.accounts.account = args.accountsAccount + // } + // if (args.accountsOwner) { + // request.accounts.owner = args.accountsOwner + // } + // } + console.log("request"); + console.log(request); + + // Send subscribe request + await new Promise((resolve, reject) => { + stream.write(request, (err) => { + if (err === null || err === undefined) { + resolve(); + } else { + reject(err); + } + }); + }).catch((reason) => { + console.error(reason); + throw reason; + }); + + await streamClosed; +} + + async function subscribeCommand(client, args) { // Subscribe for events const stream = await client.subscribe(); @@ -448,8 +564,83 @@ function parseCommandLineArgs() { }, }); }) + .command("fumarole-subscribe", "subscribe to events via fumarole", (yargs) => { + return yargs.options({ + "consumer-group-label": { + default: "", + describe: "fumarole consumer group label", + type: "string" + }, + "x-subscription-id": { + default: "", + describe: "fumarole subscription id", + type: "string" + }, + accounts: { + default: false, + describe: "subscribe on accounts updates", + type: "boolean", + }, + "accounts-account": { + default: [], + describe: "filter by account pubkey", + type: "array", + }, + "accounts-owner": { + default: [], + describe: "filter by owner pubkey", + type: "array", + }, + "accounts-memcmp": { + default: [], + describe: + "filter by offset and data, format: `offset,data in base58`", + type: "array", + }, + "accounts-datasize": { + default: 0, + describe: "filter by data size", + type: "number", + }, + "accounts-tokenaccountstate": { + default: false, + describe: "filter valid token accounts", + type: "boolean", + }, + "accounts-lamports": { + default: [], + describe: + "filter by lamports, format: `eq:42` / `ne:42` / `lt:42` / `gt:42`", + type: "array", + }, + "accounts-nonemptytxnsignature": { + description: "filter by presence of field txn_signature", + type: "boolean", + }, + "accounts-dataslice": { + default: [], + describe: + "receive only part of updated data account, format: `offset,size`", + type: "string", + }, + }); + }) .demandCommand(1) .help().argv; } +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + main(); + +function generateRandomString(length) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} From 6939e56583acbf07bb3e9669ad45e2a50e2bd3fa Mon Sep 17 00:00:00 2001 From: Wilfred Almeida Date: Thu, 12 Dec 2024 19:03:13 +0530 Subject: [PATCH 3/7] wip Signed-off-by: Wilfred Almeida --- yellowstone-grpc-client-nodejs/package.json | 4 +- yellowstone-grpc-client-nodejs/pnpm-lock.yaml | 327 ++++++++++++++++++ yellowstone-grpc-client-nodejs/src/index.ts | 76 ++++ 3 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 yellowstone-grpc-client-nodejs/pnpm-lock.yaml diff --git a/yellowstone-grpc-client-nodejs/package.json b/yellowstone-grpc-client-nodejs/package.json index 67d7748a..e39d8982 100644 --- a/yellowstone-grpc-client-nodejs/package.json +++ b/yellowstone-grpc-client-nodejs/package.json @@ -7,9 +7,9 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "build": "npm run grpc-generate && tsc -p .", + "build": "pnpm run grpc-generate && tsc -p .", "fmt": "prettier -w .", - "grpc-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc geyser.proto" + "grpc-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc fumarole.proto" }, "repository": { "type": "git", diff --git a/yellowstone-grpc-client-nodejs/pnpm-lock.yaml b/yellowstone-grpc-client-nodejs/pnpm-lock.yaml new file mode 100644 index 00000000..d91e5456 --- /dev/null +++ b/yellowstone-grpc-client-nodejs/pnpm-lock.yaml @@ -0,0 +1,327 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@grpc/grpc-js': + specifier: ^1.8.0 + version: 1.12.4 + devDependencies: + prettier: + specifier: ^2.8.3 + version: 2.8.8 + ts-proto: + specifier: ^1.139.0 + version: 1.181.2 + typescript: + specifier: ^4.9.5 + version: 4.9.5 + +packages: + + '@grpc/grpc-js@1.12.4': + resolution: {integrity: sha512-NBhrxEWnFh0FxeA0d//YP95lRFsSx2TNLEUQg4/W+5f/BMxcCjgOOIT24iD+ZB/tZw057j44DaIxja7w4XMrhg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.13': + resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} + engines: {node: '>=6'} + hasBin: true + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@types/node@22.10.1': + resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + case-anything@2.1.13: + resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==} + engines: {node: '>=12.13'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + dprint-node@1.0.8: + resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + ts-poet@6.9.0: + resolution: {integrity: sha512-roe6W6MeZmCjRmppyfOURklO5tQFQ6Sg7swURKkwYJvV7dbGCrK28um5+51iW3twdPRKtwarqFAVMU6G1mvnuQ==} + + ts-proto-descriptors@1.16.0: + resolution: {integrity: sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA==} + + ts-proto@1.181.2: + resolution: {integrity: sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg==} + hasBin: true + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + '@grpc/grpc-js@1.12.4': + dependencies: + '@grpc/proto-loader': 0.7.13 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.13': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.4.0 + yargs: 17.7.2 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@types/node@22.10.1': + dependencies: + undici-types: 6.20.0 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + case-anything@2.1.13: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + detect-libc@1.0.3: {} + + dprint-node@1.0.8: + dependencies: + detect-libc: 1.0.3 + + emoji-regex@8.0.0: {} + + escalade@3.2.0: {} + + get-caller-file@2.0.5: {} + + is-fullwidth-code-point@3.0.0: {} + + lodash.camelcase@4.3.0: {} + + long@5.2.3: {} + + prettier@2.8.8: {} + + protobufjs@7.4.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.10.1 + long: 5.2.3 + + require-directory@2.1.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + ts-poet@6.9.0: + dependencies: + dprint-node: 1.0.8 + + ts-proto-descriptors@1.16.0: + dependencies: + long: 5.2.3 + protobufjs: 7.4.0 + + ts-proto@1.181.2: + dependencies: + case-anything: 2.1.13 + protobufjs: 7.4.0 + ts-poet: 6.9.0 + ts-proto-descriptors: 1.16.0 + + typescript@4.9.5: {} + + undici-types@6.20.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 diff --git a/yellowstone-grpc-client-nodejs/src/index.ts b/yellowstone-grpc-client-nodejs/src/index.ts index 6b653b01..9578191f 100644 --- a/yellowstone-grpc-client-nodejs/src/index.ts +++ b/yellowstone-grpc-client-nodejs/src/index.ts @@ -3,6 +3,7 @@ */ // Import generated gRPC client and types. +import { CreateStaticConsumerGroupRequest, CreateStaticConsumerGroupResponse, FumaroleClient, SubscribeRequest as FumaroleSubscribeRequest } from "./grpc/fumarole"; import { CommitmentLevel, GetLatestBlockhashResponse, @@ -54,6 +55,12 @@ export { SubscribeUpdateTransactionInfo, } from "./grpc/geyser"; + +export { + SubscribeRequest as FumaroleSubscribeRequest, +} from "./grpc/fumarole" + + export default class Client { _client: GeyserClient; _insecureXToken: string | undefined; @@ -240,3 +247,72 @@ export default class Client { }); } } + +export class FumaroleSDKClient { + _client: FumaroleClient; + _insecureXToken: string | undefined; + _subscriptionId: string + + constructor( + endpoint: string, + xToken: string | undefined, + channelOptions: ChannelOptions | undefined, + subscriptionId: string + ) { + let creds: ChannelCredentials; + + const endpointURL = new URL(endpoint); + + // Check if we need to use TLS. + if (endpointURL.protocol === "https:") { + creds = credentials.combineChannelCredentials( + credentials.createSsl(), + credentials.createFromMetadataGenerator((_params, callback) => { + const metadata = new Metadata(); + if (xToken !== undefined) { + metadata.add("x-token", xToken); + } + return callback(null, metadata); + }) + ); + } else { + creds = ChannelCredentials.createInsecure(); + if (xToken !== undefined) { + this._insecureXToken = xToken; + } + } + + this._client = new FumaroleClient(endpointURL.host, creds, channelOptions); + this._subscriptionId = subscriptionId + } + + private _getInsecureMetadata(): Metadata { + const metadata = new Metadata(); + if (this._insecureXToken) { + metadata.add("x-token", this._insecureXToken); + } + + metadata.add("x-subscription-id", this._subscriptionId); + return metadata; + } + + async createConsumerGroup(request: CreateStaticConsumerGroupRequest, metadata: Metadata): Promise { + return await new Promise((resolve, reject) => { + this._client.createStaticConsumerGroup(request, metadata, (err, response) => { + if (err === null || err === undefined) { + resolve(response); + } else { + reject(err); + } + }) + }); + } + + async subscribe() { + console.log("METADATA"); + console.log(this._getInsecureMetadata()); + + return await this._client.subscribe(this._getInsecureMetadata()); + } + +} \ No newline at end of file From 96db4e109213c257d20a9831967938cf7c0edd1f Mon Sep 17 00:00:00 2001 From: Wilfred Almeida Date: Tue, 17 Dec 2024 17:20:14 +0530 Subject: [PATCH 4/7] feat: fumarole ts sdk Signed-off-by: Wilfred Almeida --- examples/typescript/README.md | 18 +- examples/typescript/src/client.ts | 206 ++++++++------------ yellowstone-grpc-client-nodejs/package.json | 2 + yellowstone-grpc-client-nodejs/src/index.ts | 60 +++++- 4 files changed, 153 insertions(+), 133 deletions(-) diff --git a/examples/typescript/README.md b/examples/typescript/README.md index 639c86ed..e4a0c89b 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -135,11 +135,23 @@ response: { version: "{\"version\":\"0.7.0+solana.1.15.2\",\"proto\":\"1.2.0+sol ## Fumarole +### Create Subscription Id +npm start -- -e="https://api.rpcpool.com" \ +fumarole-create-subscription-id + +### Get Slot Lag Info +npm start -- -e="https://api.rpcpool.com" \ +fumarole-get-slot-lag-info --subscription-id someId --consumer-group-label abcd + ### Create Consumer Group -pnpm start -- -e="http://traefik.service.ams1.consul:28080/" \ +npm start -- -e="https://api.rpcpool.com" \ create-consumer-group +### Create Consumer Group +npm start -- -e="https://api.rpcpool.com" \ +fumarole-create-consumer-group --subscription-id someId --consumer-group-label helloo + ### Subscribe -pnpm start -- -e="http://traefik.service.ams1.consul:28080/" \ +npm start -- -e="https://api.rpcpool.com" \ fumarole-subscribe \ ---accounts --accounts-account SysvarC1ock11111111111111111111111111111111 \ No newline at end of file +--accounts --accounts-account SysvarC1ock11111111111111111111111111111111 diff --git a/examples/typescript/src/client.ts b/examples/typescript/src/client.ts index f57eb0e2..c77421c4 100644 --- a/examples/typescript/src/client.ts +++ b/examples/typescript/src/client.ts @@ -1,103 +1,98 @@ import yargs from "yargs"; import Client, { CommitmentLevel, + createGrpcClient, FumaroleSDKClient, FumaroleSubscribeRequest, SubscribeRequest, SubscribeRequestFilterAccountsFilter, SubscribeRequestFilterAccountsFilterLamports, + YellowstoneGrpcClientConfig, + YellowstoneGrpcClients, } from "@triton-one/yellowstone-grpc"; import { EventSubscriptionPolicy, FumaroleClient, InitialOffsetPolicy } from "@triton-one/yellowstone-grpc/dist/grpc/fumarole"; -import { Metadata } from "@grpc/grpc-js"; async function main() { const args = parseCommandLineArgs(); - // Open connection. - const client = new Client(args.endpoint, args.xToken, { - "grpc.max_receive_message_length": 64 * 1024 * 1024, // 64MiB + const config: YellowstoneGrpcClientConfig = { + endpoint: args.endpoint, + xToken: args.xToken, + channelOptions: { + "grpc.max_receive_message_length": 1024 * 1024 * 64, // 64MiB + } + } + + // Open connection to gRPC server + + // Connections can be opened via: + // 1. Direct client initialization + // 2. Helper function + // There is no difference between these, the helper function is added to make migrations seamless + const dragonsMouthClient = new Client(args.endpoint, args.xToken, { + "grpc.max_receive_message_length": 1024 * 1024 * 64, // 64MiB }); + // const dragonsMouthClient = createGrpcClient(YellowstoneGrpcClients.DragonsMouth, config) + + const fumaroleSubscriptionId = args.subscriptionId || crypto.randomUUID() - const fumaroleSubscriptionId = crypto.randomUUID() - const fumaroleClient = new FumaroleSDKClient(args.endpoint, args.xToken, { - "grpc.max_receive_message_length": 64 * 1024 * 1024, // 64MiB, - }, fumaroleSubscriptionId); + // const fumaroleClient = new FumaroleSDKClient(args.endpoint, args.xToken, { + // "grpc.max_receive_message_length": 1024 * 1024 * 64, // 64MiB + // }, fumaroleSubscriptionId); + const fumaroleClient = createGrpcClient(YellowstoneGrpcClients.Fumarole, config, fumaroleSubscriptionId) const commitment = parseCommitmentLevel(args.commitment); // Execute a requested command switch (args["_"][0]) { case "ping": - console.log("response: " + (await client.ping(1))); + console.log("response: " + (await dragonsMouthClient.ping(1))); break; case "get-version": - console.log("response: " + (await client.getVersion())); + console.log("response: " + (await dragonsMouthClient.getVersion())); break; case "get-slot": - console.log("response: " + (await client.getSlot(commitment))); + console.log("response: " + (await dragonsMouthClient.getSlot(commitment))); break; case "get-block-height": - console.log("response: " + (await client.getBlockHeight(commitment))); + console.log("response: " + (await dragonsMouthClient.getBlockHeight(commitment))); break; case "get-latest-blockhash": - console.log("response: ", await client.getLatestBlockhash(commitment)); + console.log("response: ", await dragonsMouthClient.getLatestBlockhash(commitment)); break; case "is-blockhash-valid": - console.log("response: ", await client.isBlockhashValid(args.blockhash)); + console.log("response: ", await dragonsMouthClient.isBlockhashValid(args.blockhash)); break; case "subscribe": - await subscribeCommand(client, args); + await subscribeCommand(dragonsMouthClient, args); break; case "fumarole-subscribe": + await fumaroleSubscribeCommand(fumaroleClient, args) + break; - const metadata2: Metadata = new Metadata() - - const label2 = generateRandomString(6) - - console.log(`subscription id ${fumaroleSubscriptionId}`); - console.log(`label ${label2}`); - - metadata2.add("x-subscription-id", fumaroleSubscriptionId) - + case "fumarole-create-consumer-group": console.log(await fumaroleClient.createConsumerGroup({ commitmentLevel: CommitmentLevel.CONFIRMED, - consumerGroupLabel: label2, + consumerGroupLabel: args.consumerGroupLabel, eventSubscriptionPolicy: EventSubscriptionPolicy.BOTH, - initialOffsetPolicy: InitialOffsetPolicy.EARLIEST, - memberCount: 2 - }, metadata2)) - - - args.consumerGroupLabel = label2 - - await sleep(10000) - - await fumaroleSubscribeCommand(fumaroleClient, args) + initialOffsetPolicy: InitialOffsetPolicy.LATEST + })) break; - case "create-consumer-group": - const metadata: Metadata = new Metadata() - - const label = generateRandomString(6) - - console.log(`subscription id ${fumaroleSubscriptionId}`); - console.log(`label ${label}`); - - metadata.add("x-subscription-id", fumaroleSubscriptionId) + case "fumarole-create-subscription-id": + console.log(crypto.randomUUID()) + break; - console.log(await fumaroleClient.createConsumerGroup({ - commitmentLevel: CommitmentLevel.CONFIRMED, - consumerGroupLabel: label, - eventSubscriptionPolicy: EventSubscriptionPolicy.BOTH, - initialOffsetPolicy: InitialOffsetPolicy.EARLIEST - }, metadata)) + case "fumarole-get-slot-lag-info": + const slotLagInfo = await fumaroleClient.getSlotLagInfo({ consumerGroupLabel: args.consumerGroupLabel }) + console.log(slotLagInfo); break; default: @@ -117,8 +112,15 @@ function parseCommitmentLevel(commitment: string | undefined) { return CommitmentLevel[typedCommitment]; } - async function fumaroleSubscribeCommand(client: FumaroleSDKClient, args) { + const request: FumaroleSubscribeRequest = { + accounts: { + account: args.accountsAccount, + owner: [] + }, + consumerGroupLabel: args.consumerGroupLabel, + consumerId: 0 + }; // Subscribe for events const stream = await client.subscribe(); @@ -139,28 +141,9 @@ async function fumaroleSubscribeCommand(client: FumaroleSDKClient, args) { // Handle updates stream.on("data", (data) => { - console.log("data", data); + console.log(data); }); - // Create subscribe request based on provided arguments. - const request: FumaroleSubscribeRequest = { - transactions: { - accountKeys: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"] - }, - consumerGroupLabel: args.consumerGroupLabel, - consumerId: 1 - }; - // if (args.accounts) { - // if (args.accountsAccount) { - // request.accounts.account = args.accountsAccount - // } - // if (args.accountsOwner) { - // request.accounts.owner = args.accountsOwner - // } - // } - console.log("request"); - console.log(request); - // Send subscribe request await new Promise((resolve, reject) => { stream.write(request, (err) => { @@ -178,7 +161,6 @@ async function fumaroleSubscribeCommand(client: FumaroleSDKClient, args) { await streamClosed; } - async function subscribeCommand(client, args) { // Subscribe for events const stream = await client.subscribe(); @@ -571,7 +553,7 @@ function parseCommandLineArgs() { describe: "fumarole consumer group label", type: "string" }, - "x-subscription-id": { + "subscription-id": { default: "", describe: "fumarole subscription id", type: "string" @@ -586,61 +568,41 @@ function parseCommandLineArgs() { describe: "filter by account pubkey", type: "array", }, - "accounts-owner": { - default: [], - describe: "filter by owner pubkey", - type: "array", - }, - "accounts-memcmp": { - default: [], - describe: - "filter by offset and data, format: `offset,data in base58`", - type: "array", - }, - "accounts-datasize": { - default: 0, - describe: "filter by data size", - type: "number", - }, - "accounts-tokenaccountstate": { - default: false, - describe: "filter valid token accounts", - type: "boolean", + }); + }) + .command("fumarole-create-consumer-group", "create a fumarole consumer group", (yargs) => { + return yargs.options({ + "consumer-group-label": { + default: "", + describe: "fumarole consumer group label", + type: "string" }, - "accounts-lamports": { - default: [], - describe: - "filter by lamports, format: `eq:42` / `ne:42` / `lt:42` / `gt:42`", - type: "array", + "subscription-id": { + default: "", + describe: "fumarole subscription id", + type: "string" }, - "accounts-nonemptytxnsignature": { - description: "filter by presence of field txn_signature", - type: "boolean", + }) + }) + .command("fumarole-create-subscription-id", "create a fumarole subscription id", (yargs) => { + return yargs.options({}); + }) + .command("fumarole-get-slot-lag-info", "get fumarole slot lag info", (yargs) => { + return yargs.options({ + "consumer-group-label": { + default: "", + describe: "fumarole consumer group label", + type: "string" }, - "accounts-dataslice": { - default: [], - describe: - "receive only part of updated data account, format: `offset,size`", - type: "string", + "subscription-id": { + default: "", + describe: "fumarole subscription id", + type: "string" }, - }); + }) }) .demandCommand(1) .help().argv; } -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -main(); - -function generateRandomString(length) { - const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - let result = ''; - const charactersLength = characters.length; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; -} +main(); \ No newline at end of file diff --git a/yellowstone-grpc-client-nodejs/package.json b/yellowstone-grpc-client-nodejs/package.json index e39d8982..83dcf9af 100644 --- a/yellowstone-grpc-client-nodejs/package.json +++ b/yellowstone-grpc-client-nodejs/package.json @@ -29,7 +29,9 @@ "@grpc/grpc-js": "^1.8.0" }, "devDependencies": { + "@types/node": "^22.10.2", "prettier": "^2.8.3", + "ts-node": "^10.9.2", "ts-proto": "^1.139.0", "typescript": "^4.9.5" }, diff --git a/yellowstone-grpc-client-nodejs/src/index.ts b/yellowstone-grpc-client-nodejs/src/index.ts index 9578191f..9e46c91e 100644 --- a/yellowstone-grpc-client-nodejs/src/index.ts +++ b/yellowstone-grpc-client-nodejs/src/index.ts @@ -3,7 +3,7 @@ */ // Import generated gRPC client and types. -import { CreateStaticConsumerGroupRequest, CreateStaticConsumerGroupResponse, FumaroleClient, SubscribeRequest as FumaroleSubscribeRequest } from "./grpc/fumarole"; +import { CreateStaticConsumerGroupRequest, CreateStaticConsumerGroupResponse, FumaroleClient, GetSlotLagInfoRequest, GetSlotLagInfoResponse } from "./grpc/fumarole"; import { CommitmentLevel, GetLatestBlockhashResponse, @@ -55,11 +55,21 @@ export { SubscribeUpdateTransactionInfo, } from "./grpc/geyser"; - +// Reexport Fumarole types to distinguish them from Dragons Mouth types export { SubscribeRequest as FumaroleSubscribeRequest, } from "./grpc/fumarole" +export enum YellowstoneGrpcClients { + DragonsMouth, + Fumarole +} + +export interface YellowstoneGrpcClientConfig { + endpoint: string, + xToken: string | undefined, + channelOptions: ChannelOptions | undefined, +} export default class Client { _client: GeyserClient; @@ -296,9 +306,9 @@ export class FumaroleSDKClient { return metadata; } - async createConsumerGroup(request: CreateStaticConsumerGroupRequest, metadata: Metadata): Promise { + async createConsumerGroup(request: CreateStaticConsumerGroupRequest): Promise { return await new Promise((resolve, reject) => { - this._client.createStaticConsumerGroup(request, metadata, (err, response) => { + this._client.createStaticConsumerGroup(request, this._getInsecureMetadata(), (err, response) => { if (err === null || err === undefined) { resolve(response); } else { @@ -308,11 +318,45 @@ export class FumaroleSDKClient { }); } - async subscribe() { - console.log("METADATA"); - console.log(this._getInsecureMetadata()); + async getSlotLagInfo(request: GetSlotLagInfoRequest) { + return await new Promise((resolve, reject) => { + this._client.getSlotLagInfo(request, this._getInsecureMetadata(), (err, response) => { + if (err === null || err === undefined) { + resolve(response); + } else { + reject(err); + } + }) + }); + } + + async subscribe() { return await this._client.subscribe(this._getInsecureMetadata()); } -} \ No newline at end of file +} + +export function createGrpcClient( + type: YellowstoneGrpcClients.DragonsMouth, + config: YellowstoneGrpcClientConfig +): Client; +export function createGrpcClient( + type: YellowstoneGrpcClients.Fumarole, + config: YellowstoneGrpcClientConfig, + fumaroleSubscriptionId: string +): FumaroleSDKClient; +export function createGrpcClient( + type: YellowstoneGrpcClients, + config: YellowstoneGrpcClientConfig, + fumaroleSubscriptionId?: string +) { + switch (type) { + case YellowstoneGrpcClients.DragonsMouth: { + return new Client(config.endpoint, config.xToken, config.channelOptions); + } + case YellowstoneGrpcClients.Fumarole: { + return new FumaroleSDKClient(config.endpoint, config.xToken, config.channelOptions, fumaroleSubscriptionId); + } + } +} From 663fa6a6e1812b56d29dd3e438e2a09600ff8b7d Mon Sep 17 00:00:00 2001 From: lvboudre Date: Tue, 17 Dec 2024 14:24:37 -0500 Subject: [PATCH 5/7] fumarole: added ListConsumerGroups,DeleteConsumerGroup and (#500) --- yellowstone-grpc-proto/proto/fumarole.proto | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/yellowstone-grpc-proto/proto/fumarole.proto b/yellowstone-grpc-proto/proto/fumarole.proto index 5a28ef6d..67dd9887 100644 --- a/yellowstone-grpc-proto/proto/fumarole.proto +++ b/yellowstone-grpc-proto/proto/fumarole.proto @@ -7,11 +7,44 @@ import public "geyser.proto"; package fumarole; service Fumarole { + rpc GetConsumerGroupInfo(GetConsumerGroupInfoRequest) returns (ConsumerGroupInfo) {} + rpc ListConsumerGroups(ListConsumerGroupsRequest) returns (ListConsumerGroupsResponse) {} + rpc DeleteConsumerGroup(DeleteConsumerGroupRequest) returns (DeleteConsumerGroupResponse) {} rpc CreateStaticConsumerGroup(CreateStaticConsumerGroupRequest) returns (CreateStaticConsumerGroupResponse) {} rpc Subscribe(stream SubscribeRequest) returns (stream geyser.SubscribeUpdate) {} rpc GetSlotLagInfo(GetSlotLagInfoRequest) returns (GetSlotLagInfoResponse) {} } +message GetConsumerGroupInfoRequest { + string consumer_group_label = 1; +} + + +message DeleteConsumerGroupRequest { + string consumer_group_label = 1; +} + +message DeleteConsumerGroupResponse { + bool success = 1; +} + +message ListConsumerGroupsRequest {} + +message ListConsumerGroupsResponse { + repeated ConsumerGroupInfo consumer_groups = 1; +} + + +message ConsumerGroupInfo { + string id = 1; + string consumer_group_label = 2; + ConsumerGroupType consumer_group_type = 3; + uint32 member_count = 4; + geyser.CommitmentLevel commitment_level = 5; + EventSubscriptionPolicy event_subscription_policy = 6; + bool is_stale = 7; +} + message GetSlotLagInfoRequest { string consumer_group_label = 1; } @@ -32,6 +65,10 @@ message CreateStaticConsumerGroupResponse { string group_id = 1; } +enum ConsumerGroupType { + STATIC = 0; +} + enum InitialOffsetPolicy { EARLIEST = 0; LATEST = 1; From 862a39e2bf80a9f795a3ab183edd57d23d68931b Mon Sep 17 00:00:00 2001 From: Wilfred Almeida Date: Wed, 18 Dec 2024 13:50:39 +0530 Subject: [PATCH 6/7] refactor: fumarole-generate command Signed-off-by: Wilfred Almeida --- yellowstone-grpc-client-nodejs/package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yellowstone-grpc-client-nodejs/package.json b/yellowstone-grpc-client-nodejs/package.json index 2a350dff..cccc7ced 100644 --- a/yellowstone-grpc-client-nodejs/package.json +++ b/yellowstone-grpc-client-nodejs/package.json @@ -7,9 +7,10 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "build": "pnpm run grpc-generate && tsc -p .", + "build": "npm run grpc-generate && npm run fumarole-generate && tsc -p .", "fmt": "prettier -w .", - "grpc-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc fumarole.proto" + "grpc-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc geyser.proto", + "fumarole-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc fumarole.proto" }, "repository": { "type": "git", @@ -42,4 +43,4 @@ "files": [ "dist" ] -} +} \ No newline at end of file From 61d846eb5498dabc24b163783a8c0901d8d9aa4e Mon Sep 17 00:00:00 2001 From: Wilfred Almeida <60785452+WilfredAlmeida@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:02:36 +0000 Subject: [PATCH 7/7] feat: consumer info, delete, list functions --- examples/typescript/README.md | 28 ++++++++++ examples/typescript/src/client.ts | 62 ++++++++++++++++++++- yellowstone-grpc-client-nodejs/src/index.ts | 39 ++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/examples/typescript/README.md b/examples/typescript/README.md index e4a0c89b..9e3b7635 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -136,22 +136,50 @@ response: { version: "{\"version\":\"0.7.0+solana.1.15.2\",\"proto\":\"1.2.0+sol ## Fumarole ### Create Subscription Id +```shell npm start -- -e="https://api.rpcpool.com" \ fumarole-create-subscription-id +``` ### Get Slot Lag Info +```shell npm start -- -e="https://api.rpcpool.com" \ fumarole-get-slot-lag-info --subscription-id someId --consumer-group-label abcd +``` ### Create Consumer Group +```shell npm start -- -e="https://api.rpcpool.com" \ create-consumer-group +``` ### Create Consumer Group +```shell npm start -- -e="https://api.rpcpool.com" \ fumarole-create-consumer-group --subscription-id someId --consumer-group-label helloo +``` ### Subscribe +```shell npm start -- -e="https://api.rpcpool.com" \ fumarole-subscribe \ --accounts --accounts-account SysvarC1ock11111111111111111111111111111111 +``` + +### List Consumer Groups +```shell +npm start -- -e="https://api.rpcpool.com" \ +fumarole-list-consumer-groups --subscription-id someId +``` + +### Get Consumer Group Info +```shell +npm start -- -e="https://api.rpcpool.com" \ +fumarole-get-consumer-group-info --subscription-id someId --consumer-group-label abcd +``` + +### Delete Consumer Group +```shell +npm start -- -e="https://api.rpcpool.com" \ +fumarole-delete-consumer-group --subscription-id someId --consumer-group-label abcd +``` diff --git a/examples/typescript/src/client.ts b/examples/typescript/src/client.ts index 38c69e8b..589c00f6 100644 --- a/examples/typescript/src/client.ts +++ b/examples/typescript/src/client.ts @@ -15,6 +15,7 @@ import Client, { txErrDecode, } from "@triton-one/yellowstone-grpc"; import { EventSubscriptionPolicy, FumaroleClient, InitialOffsetPolicy } from "@triton-one/yellowstone-grpc/dist/grpc/fumarole"; +import { WasmUiTransactionEncoding } from "@triton-one/yellowstone-grpc/dist/encoding/yellowstone_grpc_solana_encoding_wasm"; async function main() { const args = parseCommandLineArgs(); @@ -99,6 +100,23 @@ async function main() { console.log(slotLagInfo); break; + case "fumarole-list-consumer-groups": + const consumerGroups = await fumaroleClient.listConsumerGroups({ consumerGroupLabel: args.consumerGroupLabel }) + console.log(consumerGroups); + break; + + case "fumarole-get-consumer-group-info": + const groupInfo = await fumaroleClient.getConsumerGroupInfo({ consumerGroupLabel: args.consumerGroupLabel }) + console.log(groupInfo); + break; + + case "fumarole-delete-consumer-group": + const deletedGroup = await fumaroleClient.deleteConsumerGroup({ consumerGroupLabel: args.consumerGroupLabel }) + console.log(deletedGroup); + break; + + + default: console.error( `Unknown command: ${args["_"]}. Use "--help" for a list of supported commands.` @@ -145,7 +163,11 @@ async function fumaroleSubscribeCommand(client: FumaroleSDKClient, args) { // Handle updates stream.on("data", (data) => { - console.log(data); + try { + const parsedTx = txEncode.encode(data.transaction.transaction, WasmUiTransactionEncoding.JsonParsed, 255, true) + console.log(JSON.stringify(parsedTx)); + } + catch (e) { } }); // Send subscribe request @@ -636,6 +658,44 @@ function parseCommandLineArgs() { }, }) }) + .command("fumarole-get-consumer-group-info", "get fumarole consumer group info", (yargs) => { + return yargs.options({ + "consumer-group-label": { + default: "", + describe: "fumarole consumer group label", + type: "string" + }, + "subscription-id": { + default: "", + describe: "fumarole subscription id", + type: "string" + }, + }) + }) + .command("fumarole-list-consumer-groups", "list fumarole consumer groups", (yargs) => { + return yargs.options({ + "subscription-id": { + default: "", + describe: "fumarole subscription id", + type: "string" + }, + }) + }) + .command("fumarole-delete-consumer-group", "delete fumarole consumer group", (yargs) => { + return yargs.options({ + "consumer-group-label": { + default: "", + describe: "fumarole consumer group label", + type: "string" + }, + "subscription-id": { + default: "", + describe: "fumarole subscription id", + type: "string" + }, + }) + }) + .demandCommand(1) .help().argv; } diff --git a/yellowstone-grpc-client-nodejs/src/index.ts b/yellowstone-grpc-client-nodejs/src/index.ts index 18efc002..5116e8d7 100644 --- a/yellowstone-grpc-client-nodejs/src/index.ts +++ b/yellowstone-grpc-client-nodejs/src/index.ts @@ -10,7 +10,7 @@ import { } from "@grpc/grpc-js"; // Import generated gRPC client and types. -import { CreateStaticConsumerGroupRequest, CreateStaticConsumerGroupResponse, FumaroleClient, GetSlotLagInfoRequest, GetSlotLagInfoResponse } from "./grpc/fumarole"; +import { ConsumerGroupInfo, CreateStaticConsumerGroupRequest, CreateStaticConsumerGroupResponse, DeleteConsumerGroupRequest, DeleteConsumerGroupResponse, FumaroleClient, GetConsumerGroupInfoRequest, GetSlotLagInfoRequest, GetSlotLagInfoResponse, ListConsumerGroupsRequest, ListConsumerGroupsResponse } from "./grpc/fumarole"; import { CommitmentLevel, GetLatestBlockhashResponse, @@ -367,7 +367,6 @@ export class FumaroleSDKClient { } async getSlotLagInfo(request: GetSlotLagInfoRequest) { - return await new Promise((resolve, reject) => { this._client.getSlotLagInfo(request, this._getInsecureMetadata(), (err, response) => { if (err === null || err === undefined) { @@ -379,6 +378,42 @@ export class FumaroleSDKClient { }); } + async listConsumerGroups(request: ListConsumerGroupsRequest) { + return await new Promise((resolve, reject) => { + this._client.listConsumerGroups(request, this._getInsecureMetadata(), (err, response) => { + if (err === null || err === undefined) { + resolve(response); + } else { + reject(err); + } + }) + }); + } + + async getConsumerGroupInfo(request: GetConsumerGroupInfoRequest) { + return await new Promise((resolve, reject) => { + this._client.getConsumerGroupInfo(request, this._getInsecureMetadata(), (err, response) => { + if (err === null || err === undefined) { + resolve(response); + } else { + reject(err); + } + }) + }); + } + + async deleteConsumerGroup(request: DeleteConsumerGroupRequest) { + return await new Promise((resolve, reject) => { + this._client.deleteConsumerGroup(request, this._getInsecureMetadata(), (err, response) => { + if (err === null || err === undefined) { + resolve(response); + } else { + reject(err); + } + }) + }); + } + async subscribe() { return await this._client.subscribe(this._getInsecureMetadata()); }