@radix-effects/transaction-stream 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # @radix-effects/transaction-stream
2
+
3
+ Stream transactions from the Radix network using [Effect](https://effect.website/).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @radix-effects/transaction-stream
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { Effect, Layer, Stream, Ref, Option, Duration } from 'effect';
15
+ import {
16
+ TransactionStreamService,
17
+ ConfigService,
18
+ TransactionDetailsOptInsSchema,
19
+ } from '@radix-effects/transaction-stream';
20
+
21
+ const program = Effect.gen(function* () {
22
+ // Create the transaction stream
23
+ const stream = yield* TransactionStreamService.pipe(
24
+ Effect.provide(TransactionStreamService.Default),
25
+ );
26
+
27
+ // Configure the stream
28
+ const configRef = yield* ConfigService.make;
29
+ yield* Ref.update(configRef, (config) => ({
30
+ ...config,
31
+ stateVersion: Option.some(1), // Start from state version 1
32
+ limitPerPage: 100,
33
+ waitTime: Duration.seconds(60),
34
+ optIns: TransactionDetailsOptInsSchema.make({
35
+ detailed_events: true,
36
+ balance_changes: true,
37
+ }),
38
+ }));
39
+
40
+ // Process transactions
41
+ yield* Stream.runForEach(stream, (transactions) =>
42
+ Effect.gen(function* () {
43
+ for (const tx of transactions) {
44
+ yield* Effect.log(`Processing tx: ${tx.intent_hash}`);
45
+ }
46
+ }),
47
+ ).pipe(
48
+ Effect.provide(Layer.effect(ConfigService, Effect.succeed(configRef))),
49
+ );
50
+ });
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ The `ConfigService` allows you to configure the transaction stream:
56
+
57
+ | Option | Type | Default | Description |
58
+ | -------------- | --------------------------- | ------------------- | --------------------------------------------- |
59
+ | `stateVersion` | `Option<number>` | `Option.none()` | Starting state version (none = current) |
60
+ | `limitPerPage` | `number` | `100` | Number of transactions per page |
61
+ | `waitTime` | `Duration` | `Duration.seconds(60)` | Wait time when no new transactions available |
62
+ | `optIns` | `TransactionDetailsOptIns` | See below | Optional data to include in response |
63
+
64
+ ### Opt-ins
65
+
66
+ Control what data is included in the transaction response:
67
+
68
+ ```typescript
69
+ TransactionDetailsOptInsSchema.make({
70
+ raw_hex: false, // Raw transaction hex
71
+ receipt_state_changes: false, // State changes in receipt
72
+ receipt_fee_summary: false, // Fee summary in receipt
73
+ receipt_fee_source: false, // Fee source in receipt
74
+ receipt_fee_destination: false, // Fee destination in receipt
75
+ receipt_costing_parameters: false, // Costing parameters in receipt
76
+ receipt_events: false, // Events in receipt (deprecated)
77
+ detailed_events: false, // Detailed events object
78
+ receipt_output: true, // Transaction receipt output
79
+ affected_global_entities: false, // Affected global entities
80
+ manifest_instructions: false, // Manifest instructions
81
+ balance_changes: false, // Fungible/non-fungible balance changes
82
+ });
83
+ ```
84
+
85
+ ## Multiple Networks
86
+
87
+ You can run multiple streams for different networks simultaneously:
88
+
89
+ ```typescript
90
+ import { ConfigProvider, Fiber } from 'effect';
91
+
92
+ const program = Effect.gen(function* () {
93
+ // Configure for Stokenet (network ID 2)
94
+ const stokenetConfig = Layer.setConfigProvider(
95
+ ConfigProvider.fromJson({ NETWORK_ID: '2' }),
96
+ );
97
+
98
+ const stokenetStream = yield* TransactionStreamService.pipe(
99
+ Effect.provide(TransactionStreamService.Default),
100
+ Effect.provide(stokenetConfig),
101
+ );
102
+
103
+ // Mainnet uses default config (network ID 1)
104
+ const mainnetStream = yield* TransactionStreamService.pipe(
105
+ Effect.provide(TransactionStreamService.Default),
106
+ );
107
+
108
+ // Run both streams concurrently
109
+ const stokenetFiber = yield* Effect.fork(
110
+ Stream.runForEach(stokenetStream, processTransactions).pipe(
111
+ Effect.provide(Layer.effect(ConfigService, Effect.succeed(stokenetConfigRef))),
112
+ ),
113
+ );
114
+
115
+ const mainnetFiber = yield* Effect.fork(
116
+ Stream.runForEach(mainnetStream, processTransactions).pipe(
117
+ Effect.provide(Layer.effect(ConfigService, Effect.succeed(mainnetConfigRef))),
118
+ ),
119
+ );
120
+
121
+ yield* Fiber.joinAll([stokenetFiber, mainnetFiber]);
122
+ });
123
+ ```
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,177 @@
1
+ import * as _radix_effects_gateway0 from "@radix-effects/gateway";
2
+ import { GatewayApiClient } from "@radix-effects/gateway";
3
+ import { Context, Duration, Effect, Layer, Option, Ref, Schema, Stream } from "effect";
4
+ import * as _radixdlt_babylon_gateway_api_sdk0 from "@radixdlt/babylon-gateway-api-sdk";
5
+ import * as effect_Effect0 from "effect/Effect";
6
+
7
+ //#region src/config.d.ts
8
+ declare const ConfigService_base: Context.TagClass<ConfigService, "Config", Ref.Ref<{
9
+ readonly stateVersion: Option.Option<number>;
10
+ readonly limitPerPage: number;
11
+ readonly waitTime: Duration.Duration;
12
+ readonly optIns: {
13
+ readonly raw_hex: boolean;
14
+ readonly receipt_state_changes: boolean;
15
+ readonly receipt_fee_summary: boolean;
16
+ readonly receipt_fee_source: boolean;
17
+ readonly receipt_fee_destination: boolean;
18
+ readonly receipt_costing_parameters: boolean;
19
+ readonly receipt_events: boolean;
20
+ readonly detailed_events: boolean;
21
+ readonly receipt_output: boolean;
22
+ readonly affected_global_entities: boolean;
23
+ readonly manifest_instructions: boolean;
24
+ readonly balance_changes: boolean;
25
+ };
26
+ }>>;
27
+ declare class ConfigService extends ConfigService_base {
28
+ static make: effect_Effect0.Effect<Ref.Ref<{
29
+ readonly stateVersion: Option.Option<number>;
30
+ readonly limitPerPage: number;
31
+ readonly waitTime: Duration.Duration;
32
+ readonly optIns: {
33
+ readonly raw_hex: boolean;
34
+ readonly receipt_state_changes: boolean;
35
+ readonly receipt_fee_summary: boolean;
36
+ readonly receipt_fee_source: boolean;
37
+ readonly receipt_fee_destination: boolean;
38
+ readonly receipt_costing_parameters: boolean;
39
+ readonly receipt_events: boolean;
40
+ readonly detailed_events: boolean;
41
+ readonly receipt_output: boolean;
42
+ readonly affected_global_entities: boolean;
43
+ readonly manifest_instructions: boolean;
44
+ readonly balance_changes: boolean;
45
+ };
46
+ }>, never, never>;
47
+ }
48
+ //#endregion
49
+ //#region src/streamer.d.ts
50
+ declare const TransactionStreamService_base: Effect.Service.Class<TransactionStreamService, "TransactionStreamService", {
51
+ readonly dependencies: readonly [Layer.Layer<GatewayApiClient, never, never>];
52
+ readonly effect: Effect.Effect<Stream.Stream<_radixdlt_babylon_gateway_api_sdk0.CommittedTransactionInfo[], _radix_effects_gateway0.InvalidRequestError | _radix_effects_gateway0.InternalServerError | _radix_effects_gateway0.NotSyncedUpError | _radix_effects_gateway0.RateLimitExceededError | _radix_effects_gateway0.UnknownGatewayError, ConfigService>, never, GatewayApiClient>;
53
+ }>;
54
+ declare class TransactionStreamService extends TransactionStreamService_base {}
55
+ //#endregion
56
+ //#region src/schemas.d.ts
57
+ declare const TransactionDetailsOptInsSchema: Schema.Struct<{
58
+ /** if set to `true`, raw transaction hex is returned. */
59
+ raw_hex: Schema.optionalWith<typeof Schema.Boolean, {
60
+ default: () => false;
61
+ }>;
62
+ /** if set to `true`, state changes inside receipt object are returned. */
63
+ receipt_state_changes: Schema.optionalWith<typeof Schema.Boolean, {
64
+ default: () => false;
65
+ }>;
66
+ /** if set to `true`, fee summary inside receipt object is returned. */
67
+ receipt_fee_summary: Schema.optionalWith<typeof Schema.Boolean, {
68
+ default: () => false;
69
+ }>;
70
+ /** if set to `true`, fee source inside receipt object is returned. */
71
+ receipt_fee_source: Schema.optionalWith<typeof Schema.Boolean, {
72
+ default: () => false;
73
+ }>;
74
+ /** if set to `true`, fee destination inside receipt object is returned. */
75
+ receipt_fee_destination: Schema.optionalWith<typeof Schema.Boolean, {
76
+ default: () => false;
77
+ }>;
78
+ /** if set to `true`, costing parameters inside receipt object is returned. */
79
+ receipt_costing_parameters: Schema.optionalWith<typeof Schema.Boolean, {
80
+ default: () => false;
81
+ }>;
82
+ /**
83
+ * if set to `true`, events inside receipt object is returned.
84
+ * @deprecated Please use `detailed_events` instead.
85
+ */
86
+ receipt_events: Schema.optionalWith<typeof Schema.Boolean, {
87
+ default: () => false;
88
+ }>;
89
+ /** if set to `true`, detailed events object is returned. */
90
+ detailed_events: Schema.optionalWith<typeof Schema.Boolean, {
91
+ default: () => false;
92
+ }>;
93
+ /** (true by default) if set to `true`, transaction receipt output is returned. */
94
+ receipt_output: Schema.optionalWith<typeof Schema.Boolean, {
95
+ default: () => true;
96
+ }>;
97
+ /** if set to `true`, all affected global entities by given transaction are returned. */
98
+ affected_global_entities: Schema.optionalWith<typeof Schema.Boolean, {
99
+ default: () => false;
100
+ }>;
101
+ /** if set to `true`, manifest instructions for user transactions are returned. */
102
+ manifest_instructions: Schema.optionalWith<typeof Schema.Boolean, {
103
+ default: () => false;
104
+ }>;
105
+ /**
106
+ * if set to `true`, returns the fungible and non-fungible balance changes.
107
+ * Warning: This opt-in might be missing for recently committed transactions.
108
+ */
109
+ balance_changes: Schema.optionalWith<typeof Schema.Boolean, {
110
+ default: () => false;
111
+ }>;
112
+ }>;
113
+ type TransactionDetailsOptIns = typeof TransactionDetailsOptInsSchema.Type;
114
+ declare const ConfigSchema: Schema.Struct<{
115
+ stateVersion: Schema.Option<typeof Schema.Number>;
116
+ limitPerPage: typeof Schema.Number;
117
+ waitTime: typeof Schema.Duration;
118
+ optIns: Schema.Struct<{
119
+ /** if set to `true`, raw transaction hex is returned. */
120
+ raw_hex: Schema.optionalWith<typeof Schema.Boolean, {
121
+ default: () => false;
122
+ }>;
123
+ /** if set to `true`, state changes inside receipt object are returned. */
124
+ receipt_state_changes: Schema.optionalWith<typeof Schema.Boolean, {
125
+ default: () => false;
126
+ }>;
127
+ /** if set to `true`, fee summary inside receipt object is returned. */
128
+ receipt_fee_summary: Schema.optionalWith<typeof Schema.Boolean, {
129
+ default: () => false;
130
+ }>;
131
+ /** if set to `true`, fee source inside receipt object is returned. */
132
+ receipt_fee_source: Schema.optionalWith<typeof Schema.Boolean, {
133
+ default: () => false;
134
+ }>;
135
+ /** if set to `true`, fee destination inside receipt object is returned. */
136
+ receipt_fee_destination: Schema.optionalWith<typeof Schema.Boolean, {
137
+ default: () => false;
138
+ }>;
139
+ /** if set to `true`, costing parameters inside receipt object is returned. */
140
+ receipt_costing_parameters: Schema.optionalWith<typeof Schema.Boolean, {
141
+ default: () => false;
142
+ }>;
143
+ /**
144
+ * if set to `true`, events inside receipt object is returned.
145
+ * @deprecated Please use `detailed_events` instead.
146
+ */
147
+ receipt_events: Schema.optionalWith<typeof Schema.Boolean, {
148
+ default: () => false;
149
+ }>;
150
+ /** if set to `true`, detailed events object is returned. */
151
+ detailed_events: Schema.optionalWith<typeof Schema.Boolean, {
152
+ default: () => false;
153
+ }>;
154
+ /** (true by default) if set to `true`, transaction receipt output is returned. */
155
+ receipt_output: Schema.optionalWith<typeof Schema.Boolean, {
156
+ default: () => true;
157
+ }>;
158
+ /** if set to `true`, all affected global entities by given transaction are returned. */
159
+ affected_global_entities: Schema.optionalWith<typeof Schema.Boolean, {
160
+ default: () => false;
161
+ }>;
162
+ /** if set to `true`, manifest instructions for user transactions are returned. */
163
+ manifest_instructions: Schema.optionalWith<typeof Schema.Boolean, {
164
+ default: () => false;
165
+ }>;
166
+ /**
167
+ * if set to `true`, returns the fungible and non-fungible balance changes.
168
+ * Warning: This opt-in might be missing for recently committed transactions.
169
+ */
170
+ balance_changes: Schema.optionalWith<typeof Schema.Boolean, {
171
+ default: () => false;
172
+ }>;
173
+ }>;
174
+ }>;
175
+ type Config = typeof ConfigSchema.Type;
176
+ //#endregion
177
+ export { Config, ConfigSchema, ConfigService, TransactionDetailsOptIns, TransactionDetailsOptInsSchema, TransactionStreamService };
package/dist/index.js ADDED
@@ -0,0 +1,95 @@
1
+ import { GatewayApiClient } from "@radix-effects/gateway";
2
+ import { Array, Context, Duration, Effect, Option, Order, Ref, Schema, Stream, pipe } from "effect";
3
+
4
+ //#region src/schemas.ts
5
+ const TransactionDetailsOptInsSchema = Schema.Struct({
6
+ raw_hex: Schema.optionalWith(Schema.Boolean, { default: () => false }),
7
+ receipt_state_changes: Schema.optionalWith(Schema.Boolean, { default: () => false }),
8
+ receipt_fee_summary: Schema.optionalWith(Schema.Boolean, { default: () => false }),
9
+ receipt_fee_source: Schema.optionalWith(Schema.Boolean, { default: () => false }),
10
+ receipt_fee_destination: Schema.optionalWith(Schema.Boolean, { default: () => false }),
11
+ receipt_costing_parameters: Schema.optionalWith(Schema.Boolean, { default: () => false }),
12
+ receipt_events: Schema.optionalWith(Schema.Boolean, { default: () => false }),
13
+ detailed_events: Schema.optionalWith(Schema.Boolean, { default: () => false }),
14
+ receipt_output: Schema.optionalWith(Schema.Boolean, { default: () => true }),
15
+ affected_global_entities: Schema.optionalWith(Schema.Boolean, { default: () => false }),
16
+ manifest_instructions: Schema.optionalWith(Schema.Boolean, { default: () => false }),
17
+ balance_changes: Schema.optionalWith(Schema.Boolean, { default: () => false })
18
+ });
19
+ const ConfigSchema = Schema.Struct({
20
+ stateVersion: Schema.Option(Schema.Number),
21
+ limitPerPage: Schema.Number,
22
+ waitTime: Schema.Duration,
23
+ optIns: TransactionDetailsOptInsSchema
24
+ });
25
+
26
+ //#endregion
27
+ //#region src/config.ts
28
+ var ConfigService = class extends Context.Tag("Config")() {
29
+ static {
30
+ this.make = Ref.make({
31
+ stateVersion: Option.none(),
32
+ limitPerPage: 100,
33
+ waitTime: Duration.seconds(60),
34
+ optIns: TransactionDetailsOptInsSchema.make()
35
+ });
36
+ }
37
+ };
38
+
39
+ //#endregion
40
+ //#region src/streamer.ts
41
+ var TransactionStreamService = class extends Effect.Service()("TransactionStreamService", {
42
+ dependencies: [GatewayApiClient.Default],
43
+ effect: Effect.gen(function* () {
44
+ const gatewayApiClient = yield* GatewayApiClient;
45
+ const currentStateVersion = yield* gatewayApiClient.status.getCurrent().pipe(Effect.catchAll(Effect.die));
46
+ yield* Effect.logDebug(currentStateVersion.ledger_state);
47
+ return Stream.paginateEffect(1, () => Effect.gen(function* () {
48
+ const configRef = yield* ConfigService;
49
+ const config = yield* configRef.pipe(Ref.get);
50
+ const stateVersion = yield* config.stateVersion.pipe(Option.match({
51
+ onNone: () => gatewayApiClient.status.getCurrent().pipe(Effect.map((res) => res.ledger_state.state_version), Effect.catchAll(Effect.die)),
52
+ onSome: (version) => Effect.succeed(version)
53
+ }));
54
+ yield* Effect.logDebug(`fetching transactions from state version ${stateVersion}`);
55
+ const result = yield* gatewayApiClient.stream.innerClient.streamTransactions({ streamTransactionsRequest: {
56
+ limit_per_page: config.limitPerPage,
57
+ from_ledger_state: { state_version: stateVersion },
58
+ order: "Asc",
59
+ kind_filter: "User",
60
+ opt_ins: config.optIns
61
+ } }).pipe(Effect.catchTags({
62
+ AccountLockerNotFoundError: Effect.die,
63
+ EntityNotFoundError: Effect.die,
64
+ ErrorResponse: Effect.die,
65
+ InvalidEntityError: Effect.die,
66
+ InvalidTransactionError: Effect.die,
67
+ ResponseError: Effect.die,
68
+ TransactionNotFoundError: Effect.die
69
+ }));
70
+ yield* Effect.logDebug(`fetched ${result.items.length} transactions`);
71
+ const firstItem = pipe(result.items, Array.sortBy(Order.mapInput(Order.number, (tx) => tx.state_version)), Array.head);
72
+ const lastItem = pipe(result.items, Array.sortBy(Order.mapInput(Order.number, (tx) => tx.state_version)), Array.last);
73
+ yield* Option.all([firstItem, lastItem]).pipe(Option.match({
74
+ onNone: () => Effect.void,
75
+ onSome: ([first, last]) => Effect.log(`${first.round_timestamp} -> ${last.round_timestamp}`)
76
+ }));
77
+ const nextStateVersion = lastItem.pipe(Option.map((res) => res.state_version + 1), Option.getOrElse(() => stateVersion));
78
+ yield* Ref.update(configRef, (config$1) => {
79
+ return {
80
+ ...config$1,
81
+ stateVersion: Option.some(nextStateVersion)
82
+ };
83
+ });
84
+ if (nextStateVersion === stateVersion) {
85
+ yield* Effect.logDebug("Waiting for new transactions...");
86
+ yield* Effect.sleep(config.waitTime);
87
+ return [[], Option.some(stateVersion)];
88
+ }
89
+ return [result.items, Option.some(nextStateVersion)];
90
+ })).pipe(Stream.filter((item) => item.length > 0));
91
+ })
92
+ }) {};
93
+
94
+ //#endregion
95
+ export { ConfigSchema, ConfigService, TransactionDetailsOptInsSchema, TransactionStreamService };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@radix-effects/transaction-stream",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/xstelea/radix-web3.js.git"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "devDependencies": {
20
+ "@types/node": "^22.13.10",
21
+ "tsdown": "^0.15.12",
22
+ "tsx": "^4.7.1",
23
+ "typescript": "^5.3.3",
24
+ "vitest": "^1.2.1"
25
+ },
26
+ "dependencies": {
27
+ "effect": "^3.14.1",
28
+ "@radix-effects/gateway": "0.3.0"
29
+ },
30
+ "license": "MIT",
31
+ "scripts": {
32
+ "dev": "tsx --watch src/index.ts",
33
+ "dev:debug": "tsx --inspect-brk --watch src/index.ts",
34
+ "build": "tsdown"
35
+ }
36
+ }