@radix-effects/tx-tool 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,900 @@
1
+ import { t as __export } from "./chunk-Bp6m_JJh.js";
2
+ import { Amount, Base64String, Epoch, FungibleResourceAddress, HexString, NetworkId, Nonce, TransactionId, TransactionManifestString, TransactionMessageString } from "@radix-effects/shared";
3
+ import { Convert, PrivateKey, PublicKey, RadixEngineToolkit, Signature, SignatureWithPublicKey, TransactionBuilder, generateRandomNonce } from "@radixdlt/radix-engine-toolkit";
4
+ import { Array, Cause, Config, Context, Data, Duration, Effect, Fiber, Layer, Option, ParseResult, Record, Redacted, Schedule, Schema, String, TestClock, flow, pipe } from "effect";
5
+ import { FileSystem, HttpBody, HttpClient } from "@effect/platform";
6
+ import { NodeFileSystem, NodeHttpClient } from "@effect/platform-node";
7
+ import { ed25519 } from "@noble/curves/ed25519.js";
8
+ import { hexToBytes } from "@noble/hashes/utils.js";
9
+ import { AccountAddress as AccountAddress$1 } from "shared/brandedTypes";
10
+ import { GatewayApiClient, GetFungibleBalance, GetLedgerStateService } from "@radix-effects/gateway";
11
+
12
+ //#region src/manifests/addFeePayer.ts
13
+ const addFeePayer = (input) => {
14
+ if (input.account.type === "unsecurifiedAccount") return TransactionManifestString.make(`
15
+ CALL_METHOD
16
+ Address("${input.account.address}")
17
+ "lock_fee"
18
+ Decimal("${input.amount}")
19
+ ;
20
+ `);
21
+ return TransactionManifestString.make(`
22
+ CALL_METHOD
23
+ Address("${input.account.accessControllerAddress}")
24
+ "create_proof"
25
+ ;
26
+
27
+ CALL_METHOD
28
+ Address("${input.account.address}")
29
+ "lock_fee"
30
+ Decimal("${input.amount}")
31
+ ;
32
+ `);
33
+ };
34
+
35
+ //#endregion
36
+ //#region src/manifests/createBadge.ts
37
+ const createBadge = (account, initialSupply = 1) => TransactionManifestString.make(`
38
+ CREATE_FUNGIBLE_RESOURCE_WITH_INITIAL_SUPPLY
39
+ Enum<0u8>()
40
+ true
41
+ 0u8
42
+ Decimal("${initialSupply}")
43
+ Tuple(
44
+ Enum<1u8>(
45
+ Tuple(
46
+ Enum<1u8>(
47
+ Enum<1u8>()
48
+ ),
49
+ Enum<1u8>(
50
+ Enum<1u8>()
51
+ )
52
+ )
53
+ ),
54
+ Enum<1u8>(
55
+ Tuple(
56
+ Enum<1u8>(
57
+ Enum<1u8>()
58
+ ),
59
+ Enum<1u8>(
60
+ Enum<1u8>()
61
+ )
62
+ )
63
+ ),
64
+ Enum<1u8>(
65
+ Tuple(
66
+ Enum<1u8>(
67
+ Enum<1u8>()
68
+ ),
69
+ Enum<1u8>(
70
+ Enum<1u8>()
71
+ )
72
+ )
73
+ ),
74
+ Enum<1u8>(
75
+ Tuple(
76
+ Enum<1u8>(
77
+ Enum<1u8>()
78
+ ),
79
+ Enum<1u8>(
80
+ Enum<1u8>()
81
+ )
82
+ )
83
+ ),
84
+ Enum<1u8>(
85
+ Tuple(
86
+ Enum<1u8>(
87
+ Enum<0u8>()
88
+ ),
89
+ Enum<1u8>(
90
+ Enum<1u8>()
91
+ )
92
+ )
93
+ ),
94
+ Enum<1u8>(
95
+ Tuple(
96
+ Enum<1u8>(
97
+ Enum<0u8>()
98
+ ),
99
+ Enum<1u8>(
100
+ Enum<1u8>()
101
+ )
102
+ )
103
+ )
104
+ )
105
+ Tuple(
106
+ Map<String, Tuple>(
107
+ "name" => Tuple(
108
+ Enum<1u8>(
109
+ Enum<0u8>(
110
+ "Badge"
111
+ )
112
+ ),
113
+ false
114
+ )
115
+ ),
116
+ Map<String, Enum>(
117
+ "metadata_setter" => Enum<0u8>(),
118
+ "metadata_setter_updater" => Enum<0u8>(),
119
+ "metadata_locker" => Enum<0u8>(),
120
+ "metadata_locker_updater" => Enum<0u8>()
121
+ )
122
+ )
123
+ Enum<0u8>()
124
+ ;
125
+
126
+ CALL_METHOD
127
+ Address("${account.address}")
128
+ "try_deposit_batch_or_abort"
129
+ Expression("ENTIRE_WORKTOP")
130
+ Enum<0u8>()
131
+ ;`);
132
+
133
+ //#endregion
134
+ //#region src/manifests/createFungibleToken.ts
135
+ const createFungibleTokenManifest = (input) => TransactionManifestString.make(`CREATE_FUNGIBLE_RESOURCE_WITH_INITIAL_SUPPLY
136
+ None
137
+ true
138
+ 0u8
139
+ Decimal("${input.initialSupply}")
140
+ Tuple(
141
+ Some(
142
+ Tuple(
143
+ Some(Enum<AccessRule::DenyAll>()),
144
+ Some(Enum<AccessRule::DenyAll>())
145
+ )
146
+ ),
147
+ Some(
148
+ Tuple(
149
+ Some(Enum<AccessRule::DenyAll>()),
150
+ Some(Enum<AccessRule::DenyAll>())
151
+ )
152
+ ),
153
+ Some(
154
+ Tuple(
155
+ Some(Enum<AccessRule::DenyAll>()),
156
+ Some(Enum<AccessRule::DenyAll>())
157
+ )
158
+ ),
159
+ Some(
160
+ Tuple(
161
+ Some(Enum<AccessRule::DenyAll>()),
162
+ Some(Enum<AccessRule::DenyAll>())
163
+ )
164
+ ),
165
+ Some(
166
+ Tuple(
167
+ Some(Enum<AccessRule::AllowAll>()),
168
+ Some(Enum<AccessRule::DenyAll>())
169
+ )
170
+ ),
171
+ Some(
172
+ Tuple(
173
+ Some(Enum<AccessRule::AllowAll>()),
174
+ Some(Enum<AccessRule::DenyAll>())
175
+ )
176
+ )
177
+ )
178
+ Tuple(
179
+ Map<String, Tuple>(
180
+ "name" => Tuple(
181
+ Some(Enum<Metadata::String>("${input.name}")),
182
+ false
183
+ ),"symbol" => Tuple(
184
+ Some(Enum<Metadata::String>("${input.symbol}")),
185
+ false
186
+ )
187
+ ),
188
+ Map<String, Enum>(
189
+ "metadata_setter" => None,
190
+ "metadata_setter_updater" => None,
191
+ "metadata_locker" => None,
192
+ "metadata_locker_updater" => None
193
+ )
194
+ )
195
+ None
196
+ ;
197
+
198
+ CALL_METHOD
199
+ Address("${input.account.address}")
200
+ "try_deposit_batch_or_abort"
201
+ Expression("ENTIRE_WORKTOP")
202
+ Enum<0u8>()
203
+ ;`);
204
+
205
+ //#endregion
206
+ //#region src/manifests/faucet.ts
207
+ const faucet = (accountAddress) => Effect.gen(function* () {
208
+ const knownAddresses = yield* Effect.tryPromise(() => RadixEngineToolkit.Utils.knownAddresses(2));
209
+ return TransactionManifestString.make(`
210
+ CALL_METHOD
211
+ Address("${knownAddresses.componentAddresses.faucet}")
212
+ "lock_fee"
213
+ Decimal("10")
214
+ ;
215
+
216
+ CALL_METHOD
217
+ Address("${knownAddresses.componentAddresses.faucet}")
218
+ "free"
219
+ ;
220
+
221
+ CALL_METHOD
222
+ Address("${accountAddress}")
223
+ "try_deposit_batch_or_abort"
224
+ Expression("ENTIRE_WORKTOP")
225
+ Enum<0u8>()
226
+ ;`);
227
+ });
228
+
229
+ //#endregion
230
+ //#region src/manifests/manifestHelper.ts
231
+ var ManifestHelper = class extends Effect.Service()("ManifestHelper", { effect: Effect.gen(function* () {
232
+ return { addFeePayer: (input) => Effect.gen(function* () {
233
+ if (input.account.type === "unsecurifiedAccount") return TransactionManifestString.make(`
234
+ CALL_METHOD
235
+ Address("${input.account.address}")
236
+ "lock_fee"
237
+ Decimal("${input.amount}")
238
+ ;
239
+ `);
240
+ return TransactionManifestString.make(`
241
+ CALL_METHOD
242
+ Address("${input.account.accessControllerAddress}")
243
+ "create_proof"
244
+ ;
245
+
246
+ CALL_METHOD
247
+ Address("${input.account.address}")
248
+ "lock_fee"
249
+ Decimal("${input.amount}")
250
+ ;
251
+ `);
252
+ }) };
253
+ }) }) {};
254
+
255
+ //#endregion
256
+ //#region src/schemas.ts
257
+ var schemas_exports = /* @__PURE__ */ __export({
258
+ BadgeDecodedSchema: () => BadgeDecodedSchema,
259
+ BadgeSchema: () => BadgeSchema,
260
+ Base64FromHexSchema: () => Base64FromHexSchema,
261
+ Ed25519PrivateKeySchema: () => Ed25519PrivateKeySchema,
262
+ Ed25519PublicKeySchema: () => Ed25519PublicKeySchema,
263
+ Ed25519SignatureWithPublicKeySchema: () => Ed25519SignatureWithPublicKeySchema,
264
+ HexFromBase64Schema: () => HexFromBase64Schema,
265
+ ManifestSchema: () => ManifestSchema,
266
+ TransactionHeaderSchema: () => TransactionHeaderSchema,
267
+ TransactionIntentSchema: () => TransactionIntentSchema,
268
+ TransactionMessageSchema: () => TransactionMessageSchema
269
+ });
270
+ const Base64FromHexSchema = Schema.asSchema(Schema.transformOrFail(HexString, Base64String, {
271
+ decode: (hex) => Effect.succeed(Base64String.make(Buffer.from(hex, "hex").toString("base64"))),
272
+ encode: (base64) => Effect.succeed(HexString.make(Buffer.from(base64, "base64").toString("hex")))
273
+ }));
274
+ const HexFromBase64Schema = Schema.asSchema(Schema.transformOrFail(Base64String, HexString, {
275
+ decode: (base64) => Effect.succeed(HexString.make(Buffer.from(base64, "base64").toString("hex"))),
276
+ encode: (hex) => Effect.succeed(Base64String.make(Buffer.from(hex, "hex").toString("base64")))
277
+ }));
278
+ const Ed25519PublicKeySchema = Schema.asSchema(Schema.transformOrFail(HexString, Schema.instanceOf(PublicKey.Ed25519), {
279
+ decode: (hex) => Effect.succeed(new PublicKey.Ed25519(hex)),
280
+ encode: (publicKey) => Effect.succeed(HexString.make(publicKey.hex()))
281
+ }));
282
+ const Ed25519PrivateKeySchema = Schema.asSchema(Schema.transformOrFail(HexString, Schema.instanceOf(PrivateKey.Ed25519), {
283
+ decode: (hex) => Effect.succeed(new PrivateKey.Ed25519(hex)),
284
+ encode: (publicKey) => Effect.succeed(HexString.make(Convert.Uint8Array.toHexString(publicKey.bytes)))
285
+ }));
286
+ const ManifestSchema = Schema.asSchema(Schema.transformOrFail(TransactionManifestString, Schema.Struct({
287
+ instructions: Schema.Struct({
288
+ kind: Schema.Literal("String"),
289
+ value: TransactionManifestString
290
+ }),
291
+ blobs: Schema.mutable(Schema.Array(Schema.Uint8Array))
292
+ }), {
293
+ decode: (value) => Effect.succeed({
294
+ instructions: {
295
+ kind: "String",
296
+ value
297
+ },
298
+ blobs: []
299
+ }),
300
+ encode: (input) => Effect.succeed(TransactionManifestString.make(input.instructions.value))
301
+ }));
302
+ const PlainTextMessageSchema = Schema.Struct({
303
+ kind: Schema.Literal("PlainText"),
304
+ value: Schema.Struct({
305
+ message: Schema.Struct({
306
+ kind: Schema.Literal("String"),
307
+ value: TransactionMessageString
308
+ }),
309
+ mimeType: Schema.Literal("text/plain")
310
+ })
311
+ });
312
+ const EmptyMessageSchema = Schema.Struct({ kind: Schema.Literal("None") });
313
+ const TransactionMessageSchema = Schema.asSchema(Schema.transformOrFail(Schema.OptionFromUndefinedOr(TransactionMessageString), Schema.Union(PlainTextMessageSchema, EmptyMessageSchema), {
314
+ decode: (value) => Option.match(value, {
315
+ onNone: () => Effect.succeed({ kind: "None" }),
316
+ onSome: (value$1) => Effect.succeed({
317
+ kind: "PlainText",
318
+ value: {
319
+ message: {
320
+ kind: "String",
321
+ value: value$1
322
+ },
323
+ mimeType: "text/plain"
324
+ }
325
+ })
326
+ }),
327
+ encode: (input) => Effect.succeed(input.kind === "None" ? Option.none() : Option.some(TransactionMessageString.make(input.value.message.value)))
328
+ }));
329
+ const TransactionHeaderSchema = Schema.Struct({
330
+ networkId: NetworkId,
331
+ startEpochInclusive: Epoch,
332
+ endEpochExclusive: Epoch,
333
+ notaryPublicKey: Ed25519PublicKeySchema,
334
+ nonce: Nonce,
335
+ notaryIsSignatory: Schema.Boolean,
336
+ tipPercentage: Schema.Number
337
+ });
338
+ const TransactionIntentSchema = Schema.Struct({
339
+ header: TransactionHeaderSchema,
340
+ message: TransactionMessageSchema,
341
+ manifest: ManifestSchema
342
+ });
343
+ const Ed25519SignatureWithPublicKeySchema = Schema.asSchema(Schema.transformOrFail(Schema.Struct({
344
+ signature: HexString,
345
+ signerPublicKey: HexString,
346
+ curve: Schema.Literal("Ed25519")
347
+ }), Schema.instanceOf(SignatureWithPublicKey.Ed25519), {
348
+ strict: false,
349
+ decode: (value) => Effect.succeed(new SignatureWithPublicKey.Ed25519(value.signature, value.signerPublicKey)),
350
+ encode: (input) => Effect.succeed({
351
+ signature: HexString.make(Convert.Uint8Array.toHexString(input.signature)),
352
+ signerPublicKey: HexString.make(Convert.Uint8Array.toHexString(input.publicKey))
353
+ })
354
+ }));
355
+ const BadgeDecodedSchema = Schema.Struct({
356
+ type: Schema.Literal("fungibleResource"),
357
+ resourceAddress: FungibleResourceAddress
358
+ });
359
+ const BadgeSchema = Schema.transformOrFail(Schema.Struct({
360
+ type: Schema.String,
361
+ resourceAddress: Schema.String
362
+ }), BadgeDecodedSchema, {
363
+ strict: false,
364
+ decode: (value) => Effect.succeed(Schema.Struct({
365
+ type: Schema.Literal("fungibleResource"),
366
+ resourceAddress: FungibleResourceAddress
367
+ }).make({
368
+ type: "fungibleResource",
369
+ resourceAddress: FungibleResourceAddress.make(value.resourceAddress)
370
+ })),
371
+ encode: (value) => Effect.succeed(value)
372
+ });
373
+
374
+ //#endregion
375
+ //#region src/signer/vault.ts
376
+ const SignResponseSchema = Schema.asSchema(Schema.transformOrFail(Schema.Struct({ data: Schema.Struct({ signature: Schema.String }) }), HexString, {
377
+ strict: true,
378
+ decode: (input) => pipe(input.data.signature, String.split(":"), Array.last, Option.getOrThrow, Base64String.make, Schema.decode(HexFromBase64Schema), Effect.catchTag("ParseError", (error) => ParseResult.fail(error.issue))),
379
+ encode: (value, _, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, value, "Encoding signatures back to response format is forbidden."))
380
+ }));
381
+ const PublicKeyResponseSchema = Schema.asSchema(Schema.transformOrFail(Schema.Struct({ data: Schema.Struct({ keys: Schema.Record({
382
+ key: Schema.String,
383
+ value: Schema.Struct({ public_key: Base64String })
384
+ }) }) }), HexString, {
385
+ strict: true,
386
+ decode: (input) => pipe(input.data.keys, (keys) => Record.values(keys), Array.head, Option.map((item) => item.public_key), Option.getOrThrow, Schema.decode(HexFromBase64Schema), Effect.catchTag("ParseError", (error) => ParseResult.fail(error.issue))),
387
+ encode: (value, _, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, value, "Encoding public keys back to response format is forbidden."))
388
+ }));
389
+ /**
390
+ * Reads the Vault token from a file or falls back to environment variable.
391
+ * In production, Vault Agent writes the token to a file and handles renewal.
392
+ * For local development, you can use either VAULT_TOKEN_FILE or VAULT_TOKEN.
393
+ */
394
+ const getVaultToken = (fs) => Effect.gen(function* () {
395
+ const tokenFilePath = yield* Config.string("VAULT_TOKEN_FILE").pipe(Config.option, Effect.map(Option.getOrUndefined));
396
+ if (tokenFilePath) return (yield* fs.readFileString(tokenFilePath)).trim();
397
+ return yield* Config.string("VAULT_TOKEN");
398
+ });
399
+ const NodeHttpClientLive = NodeHttpClient.layerWithoutAgent.pipe(Layer.provide(NodeHttpClient.makeAgentLayer({ rejectUnauthorized: false })));
400
+ var Vault = class extends Effect.Service()("Vault", {
401
+ dependencies: [NodeHttpClientLive, NodeFileSystem.layer],
402
+ effect: Effect.gen(function* () {
403
+ const keyName = yield* Config.string("VAULT_KEY_NAME").pipe(Config.withDefault("xrd-distribution"));
404
+ const baseUrl = yield* Config.string("VAULT_BASE_URL").pipe(Config.withDefault("http://localhost:8200"), Effect.orDie);
405
+ const httpClient = (yield* HttpClient.HttpClient).pipe(HttpClient.retryTransient({
406
+ schedule: Schedule.exponential(Duration.millis(100)),
407
+ times: 3
408
+ }), HttpClient.tapError((error) => Effect.logError("Vault request error", { error })), HttpClient.tap((response) => response.status >= 200 && response.status < 300 ? Effect.void : response.json.pipe(Effect.tap((body) => Effect.logError("Vault request failed", {
409
+ status: response.status,
410
+ url: response.request.url,
411
+ body
412
+ })), Effect.ignore)), HttpClient.filterStatusOk);
413
+ const fs = yield* FileSystem.FileSystem;
414
+ const getPublicKey = () => Effect.gen(function* () {
415
+ const token = yield* getVaultToken(fs);
416
+ return yield* httpClient.get(`${baseUrl}/v1/transit/keys/${keyName}`, { headers: { "X-Vault-Token": token } }).pipe(Effect.tapError(Effect.logError), Effect.flatMap((response) => response.json), Effect.flatMap(Schema.decodeUnknown(PublicKeyResponseSchema)));
417
+ }).pipe(Effect.orDie);
418
+ const toSignatureWithPublicKey = (hash) => Effect.gen(function* () {
419
+ const token = yield* getVaultToken(fs);
420
+ const encoded = {
421
+ signature: yield* httpClient.post(`${baseUrl}/v1/transit/sign/${keyName}`, {
422
+ headers: {
423
+ "X-Vault-Token": token,
424
+ "Content-Type": "application/json"
425
+ },
426
+ body: yield* Schema.decode(Base64FromHexSchema)(hash).pipe(Effect.flatMap((base64) => HttpBody.json({ input: base64 })))
427
+ }).pipe(Effect.flatMap((response) => response.json), Effect.tap((response) => Effect.log(response)), Effect.flatMap(Schema.decodeUnknown(SignResponseSchema))),
428
+ signerPublicKey: yield* getPublicKey(),
429
+ curve: "Ed25519"
430
+ };
431
+ return yield* Schema.decode(Ed25519SignatureWithPublicKeySchema)(encoded);
432
+ }).pipe(Effect.annotateLogs({
433
+ signer: "Vault",
434
+ keyName
435
+ }));
436
+ return {
437
+ getPublicKey,
438
+ toSignatureWithPublicKey
439
+ };
440
+ })
441
+ }) {};
442
+
443
+ //#endregion
444
+ //#region src/signer/signer.ts
445
+ var FailedToSignTransactionError = class extends Data.TaggedError("FailedToSignTransactionError") {};
446
+ var Signer = class Signer extends Context.Tag("Signer")() {
447
+ static VaultLive = Layer.effect(Signer, Effect.gen(function* () {
448
+ const vault = yield* Vault;
449
+ return {
450
+ signToSignatureWithPublicKey: (hash) => vault.toSignatureWithPublicKey(hash).pipe(Effect.map((signatureWithPublicKey) => [signatureWithPublicKey]), Effect.catchAll(Effect.die)),
451
+ publicKey: () => vault.getPublicKey().pipe(Effect.map((publicKey) => new PublicKey.Ed25519(publicKey)))
452
+ };
453
+ })).pipe(Layer.provide(Vault.Default));
454
+ static makePrivateKeySigner = (privateKey) => Layer.effect(Signer, Effect.gen(function* () {
455
+ return {
456
+ signToSignatureWithPublicKey: (hash) => Effect.gen(function* () {
457
+ const value = Redacted.value(privateKey);
458
+ const Ed25519PrivateKey = yield* Schema.decode(Ed25519PrivateKeySchema)(value);
459
+ return [{
460
+ curve: "Ed25519",
461
+ signature: Ed25519PrivateKey.signToSignatureWithPublicKey(Convert.HexString.toUint8Array(hash)).signature,
462
+ publicKey: Ed25519PrivateKey.publicKey().bytes
463
+ }];
464
+ }).pipe(Effect.orDie, Effect.annotateLogs({ signer: "PrivateKey" })),
465
+ publicKey: () => Effect.gen(function* () {
466
+ const value = Redacted.value(privateKey);
467
+ return (yield* Schema.decode(Ed25519PrivateKeySchema)(value)).publicKey();
468
+ }).pipe(Effect.orDie)
469
+ };
470
+ }));
471
+ };
472
+
473
+ //#endregion
474
+ //#region src/test-helpers/createAccount.ts
475
+ const createAccount = (input) => Effect.gen(function* () {
476
+ const privateKey = input?.privateKey ? input.privateKey : ed25519.keygen().secretKey;
477
+ const publicKey = ed25519.getPublicKey(privateKey);
478
+ const publicKeyHex = Buffer.from(publicKey).toString("hex");
479
+ const keypair = new PrivateKey.Ed25519(privateKey);
480
+ const address = yield* Effect.tryPromise(() => RadixEngineToolkit.Derive.virtualAccountAddressFromPublicKey(keypair.publicKey(), input?.networkId ?? 1));
481
+ return {
482
+ address: AccountAddress$1.make(address),
483
+ sign: (hash) => {
484
+ const signature = ed25519.sign(hexToBytes(hash), privateKey);
485
+ return Buffer.from(signature).toString("hex");
486
+ },
487
+ publicKeyHex,
488
+ privateKeyHex: Buffer.from(privateKey).toString("hex")
489
+ };
490
+ });
491
+
492
+ //#endregion
493
+ //#region src/test-helpers/disableTestClock.ts
494
+ const resolveFiber = (fiber) => Effect.gen(function* () {
495
+ while (Option.isNone(yield* Fiber.poll(fiber))) {
496
+ yield* Effect.promise(async () => new Promise((resolve) => setTimeout(resolve, 1e3)));
497
+ yield* TestClock.adjust(Duration.seconds(1));
498
+ }
499
+ return yield* Fiber.join(fiber);
500
+ });
501
+ const DisableTestClock = (effect) => Effect.gen(function* () {
502
+ return yield* resolveFiber(yield* effect.pipe(Effect.fork));
503
+ });
504
+
505
+ //#endregion
506
+ //#region src/notaryKeyPair.ts
507
+ var NotaryKeyPair = class extends Effect.Service()("NotaryKeyPair", { effect: Effect.gen(function* () {
508
+ const signer = yield* Signer;
509
+ return {
510
+ publicKey: () => signer.publicKey(),
511
+ signToSignature: (hash) => signer.signToSignatureWithPublicKey(hash).pipe(Effect.map(flow(Array.head, Option.map((signature) => new Signature.Ed25519(signature.signature)), Option.getOrThrow)))
512
+ };
513
+ }) }) {};
514
+
515
+ //#endregion
516
+ //#region src/compileTransaction.ts
517
+ var FailedToCompileTransactionError = class extends Data.TaggedError("FailedToCompileTransactionError") {};
518
+ var FailedToNotarizeTransactionError = class extends Data.TaggedError("FailedToNotarizeTransactionError") {};
519
+ Schema.Struct({
520
+ intent: TransactionIntentSchema,
521
+ signatures: Schema.Array(Ed25519SignatureWithPublicKeySchema)
522
+ });
523
+ var CompileTransaction = class extends Effect.Service()("CompileTransaction", {
524
+ dependencies: [NotaryKeyPair.Default],
525
+ effect: Effect.gen(function* () {
526
+ const notaryKeyPair = yield* NotaryKeyPair;
527
+ const TransactionBuilder$1 = Effect.tryPromise(() => TransactionBuilder.new()).pipe(Effect.catchAll(Effect.orDie));
528
+ const notarySignToSignature = (hash) => pipe(Convert.Uint8Array.toHexString(hash), HexString.make, notaryKeyPair.signToSignature).pipe(Effect.runPromise);
529
+ const notarizeTransaction = (input) => Effect.gen(function* () {
530
+ const builder = yield* TransactionBuilder$1;
531
+ return yield* Effect.tryPromise({
532
+ try: () => pipe(builder, (builder$1) => builder$1.header(input.intent.header), (builder$1) => builder$1.message(input.intent.message), (builder$1) => builder$1.manifest(input.intent.manifest), (builder$1) => input.signatures.reduce((builder$2, signature) => builder$2.sign(signature), builder$1), (builder$1) => builder$1.notarizeAsync(notarySignToSignature)),
533
+ catch: (error) => new FailedToNotarizeTransactionError({ error })
534
+ });
535
+ });
536
+ const compileNotarizedTransaction = (input) => Effect.tryPromise({
537
+ try: () => RadixEngineToolkit.NotarizedTransaction.compile(input),
538
+ catch: (error) => new FailedToCompileTransactionError({ error })
539
+ });
540
+ return (input) => notarizeTransaction(input).pipe(Effect.flatMap(compileNotarizedTransaction));
541
+ })
542
+ }) {};
543
+
544
+ //#endregion
545
+ //#region src/staticallyValidateManifest.ts
546
+ var FailedToStaticallyValidateManifestError = class extends Data.TaggedError("FailedToStaticallyValidateManifestError") {};
547
+ var InvalidManifestError = class extends Data.TaggedError("InvalidManifestError") {};
548
+ var StaticallyValidateManifest = class extends Effect.Service()("StaticallyValidateManifest", { effect: Effect.gen(function* () {
549
+ return (input) => Effect.gen(function* () {
550
+ const result = yield* Effect.tryPromise({
551
+ try: () => RadixEngineToolkit.TransactionManifest.staticallyValidate(input.manifest, input.networkId),
552
+ catch: (error) => {
553
+ console.error(error);
554
+ return new FailedToStaticallyValidateManifestError({ error });
555
+ }
556
+ });
557
+ if (result.kind === "Invalid") return yield* Effect.fail(new InvalidManifestError({ message: result.error }));
558
+ });
559
+ }) }) {};
560
+
561
+ //#endregion
562
+ //#region src/epoch.ts
563
+ var InvalidEndEpochError = class extends Data.TaggedError("InvalidEndEpochError") {};
564
+ var InvalidStartEpochError = class extends Data.TaggedError("InvalidStartEpochError") {};
565
+ var EpochService = class extends Effect.Service()("EpochService", {
566
+ dependencies: [GetLedgerStateService.Default],
567
+ effect: Effect.gen(function* () {
568
+ const getLedgerStateService = yield* GetLedgerStateService;
569
+ const getCurrentEpoch = () => Effect.gen(function* () {
570
+ return yield* getLedgerStateService({ at_ledger_state: { timestamp: /* @__PURE__ */ new Date() } }).pipe(Effect.map((ledgerState) => Epoch.make(ledgerState.epoch)));
571
+ });
572
+ return {
573
+ getCurrentEpoch,
574
+ verifyEpochBounds: (input) => Effect.gen(function* () {
575
+ const currentEpoch = yield* getCurrentEpoch();
576
+ if (currentEpoch < input.transactionIntent.header.startEpochInclusive) return yield* new InvalidStartEpochError({
577
+ message: `Current epoch ${currentEpoch} is less than start epoch ${input.transactionIntent.header.startEpochInclusive}`,
578
+ transactionId: input.transactionId
579
+ });
580
+ if (currentEpoch >= input.transactionIntent.header.endEpochExclusive) return yield* new InvalidEndEpochError({
581
+ message: `Current epoch ${currentEpoch} is greater than or equal to end epoch ${input.transactionIntent.header.endEpochExclusive}`,
582
+ transactionId: input.transactionId
583
+ });
584
+ })
585
+ };
586
+ })
587
+ }) {};
588
+
589
+ //#endregion
590
+ //#region src/transactionHeader.ts
591
+ var InvalidEpochError = class extends Data.TaggedError("InvalidEpochError") {};
592
+ var TransactionHeader = class extends Effect.Service()("TransactionHeader", {
593
+ dependencies: [
594
+ GetLedgerStateService.Default,
595
+ NotaryKeyPair.Default,
596
+ EpochService.Default
597
+ ],
598
+ effect: Effect.gen(function* () {
599
+ const notaryKeyPair = yield* NotaryKeyPair;
600
+ const epochService = yield* EpochService;
601
+ const generateNonce = () => pipe(generateRandomNonce(), Nonce.make);
602
+ return (input) => Effect.gen(function* () {
603
+ const { tipPercentage = 0, nonce = generateNonce(), notaryIsSignatory = false } = input;
604
+ const currentEpoch = yield* epochService.getCurrentEpoch();
605
+ const startEpochInclusive = Option.match(input.startEpochInclusive, {
606
+ onSome: (epoch) => epoch,
607
+ onNone: () => currentEpoch
608
+ });
609
+ if (currentEpoch < startEpochInclusive) return yield* new InvalidEpochError({ message: `Current epoch ${currentEpoch} is less than start epoch ${input.startEpochInclusive}` });
610
+ const endEpochExclusive = Option.match(input.endEpochExclusive, {
611
+ onSome: (epoch) => epoch,
612
+ onNone: () => Epoch.make(currentEpoch + 2)
613
+ });
614
+ if (currentEpoch >= endEpochExclusive) return yield* new InvalidEpochError({ message: `Current epoch ${currentEpoch} is greater than or equal to end epoch ${input.endEpochExclusive}` });
615
+ const notaryPublicKey = yield* notaryKeyPair.publicKey();
616
+ return TransactionHeaderSchema.make({
617
+ networkId: input.networkId,
618
+ startEpochInclusive,
619
+ endEpochExclusive,
620
+ notaryPublicKey,
621
+ nonce,
622
+ notaryIsSignatory,
623
+ tipPercentage
624
+ });
625
+ });
626
+ })
627
+ }) {};
628
+
629
+ //#endregion
630
+ //#region src/createTransactionIntent.ts
631
+ const CreateTransactionIntentInputSchema = Schema.Struct({
632
+ startEpochInclusive: Schema.optional(Epoch),
633
+ endEpochExclusive: Schema.optional(Epoch),
634
+ manifest: TransactionManifestString,
635
+ message: Schema.optional(TransactionMessageString),
636
+ tipPercentage: Schema.optional(Schema.Number)
637
+ });
638
+ var CreateTransactionIntent = class extends Effect.Service()("CreateTransactionIntent", {
639
+ dependencies: [StaticallyValidateManifest.Default, TransactionHeader.Default],
640
+ effect: Effect.gen(function* () {
641
+ const staticallyValidateManifest = yield* StaticallyValidateManifest;
642
+ const createTransactionHeader = yield* TransactionHeader;
643
+ const gatewayApiClient = yield* GatewayApiClient;
644
+ const networkId = NetworkId.make(gatewayApiClient.networkId);
645
+ return (input) => Effect.gen(function* () {
646
+ const parsedInput = yield* Schema.decodeUnknown(CreateTransactionIntentInputSchema)(input);
647
+ const header = yield* createTransactionHeader({
648
+ networkId,
649
+ startEpochInclusive: Option.fromNullable(parsedInput.startEpochInclusive),
650
+ endEpochExclusive: Option.fromNullable(parsedInput.endEpochExclusive)
651
+ });
652
+ const message = yield* Schema.decodeUnknown(TransactionMessageSchema)(parsedInput.message);
653
+ const manifest = yield* Schema.decodeUnknown(ManifestSchema)(parsedInput.manifest);
654
+ const transactionIntent = {
655
+ header,
656
+ message,
657
+ manifest
658
+ };
659
+ yield* staticallyValidateManifest({
660
+ manifest,
661
+ networkId
662
+ });
663
+ return TransactionIntentSchema.make(transactionIntent);
664
+ });
665
+ })
666
+ }) {};
667
+
668
+ //#endregion
669
+ //#region src/intentHash.ts
670
+ var FailedToCreateIntentHashError = class extends Data.TaggedError("FailedToCreateIntentHashError") {};
671
+ var IntentHashService = class extends Effect.Service()("IntentHashService", { effect: Effect.gen(function* () {
672
+ const createIntentHash = (input) => Effect.tryPromise({
673
+ try: () => RadixEngineToolkit.Intent.hash(input),
674
+ catch: (error) => new FailedToCreateIntentHashError({ error })
675
+ }).pipe(Effect.map((hash) => ({
676
+ id: TransactionId.make(hash.id),
677
+ hash: pipe(Convert.Uint8Array.toHexString(hash.hash), HexString.make)
678
+ })));
679
+ return { create: createIntentHash };
680
+ }) }) {};
681
+
682
+ //#endregion
683
+ //#region src/previewTransaction.ts
684
+ var TransactionPreviewError = class extends Data.TaggedError("TransactionPreviewError") {};
685
+ var PreviewTransaction = class extends Effect.Service()("PreviewTransaction", { effect: Effect.gen(function* () {
686
+ const gatewayApiClient = yield* GatewayApiClient;
687
+ return (input) => Effect.gen(function* () {
688
+ const result = yield* gatewayApiClient.transaction.innerClient.transactionPreview({ transactionPreviewRequest: input.payload });
689
+ const receipt = result.receipt;
690
+ if (receipt.status !== "Succeeded") return yield* new TransactionPreviewError({ message: receipt.error_message });
691
+ return result;
692
+ });
693
+ }) }) {};
694
+
695
+ //#endregion
696
+ //#region src/submitTransaction.ts
697
+ var SubmitTransaction = class extends Effect.Service()("SubmitTransaction", { effect: Effect.gen(function* () {
698
+ const gatewayApiClient = yield* GatewayApiClient;
699
+ return (input) => Effect.gen(function* () {
700
+ const notarizedTransactionHex = pipe(input.compiledTransaction, Convert.Uint8Array.toHexString, HexString.make);
701
+ return yield* gatewayApiClient.transaction.innerClient.transactionSubmit({ transactionSubmitRequest: { notarized_transaction_hex: notarizedTransactionHex } });
702
+ });
703
+ }) }) {};
704
+
705
+ //#endregion
706
+ //#region src/transactionStatus.ts
707
+ var TransactionNotResolvedError = class extends Data.TaggedError("TransactionNotResolvedError") {};
708
+ var TransactionFailedError = class extends Data.TaggedError("TransactionFailedError") {};
709
+ var TimeoutError = class extends Data.TaggedError("TimeoutError") {};
710
+ var TransactionStatus = class extends Effect.Service()("TransactionStatus", { effect: Effect.gen(function* () {
711
+ const pollTimeoutDuration = yield* Config.duration("TRANSACTION_STATUS_POLL_TIMEOUT").pipe(Config.withDefault(Duration.minutes(1)), Effect.orDie);
712
+ const maxPollAttempts = yield* Config.number("TRANSACTION_STATUS_MAX_POLL_ATTEMPTS_COUNT").pipe(Config.withDefault(10), Effect.orDie);
713
+ const pollDelay = yield* Config.duration("TRANSACTION_STATUS_POLL_DELAY").pipe(Config.withDefault(Duration.millis(100)), Effect.orDie);
714
+ const retryPolicy = Schedule.exponential(pollDelay).pipe(Schedule.compose(Schedule.recurs(maxPollAttempts)));
715
+ const gatewayApiClient = yield* GatewayApiClient;
716
+ const getTransactionStatus = (id) => Effect.gen(function* () {
717
+ const result = yield* gatewayApiClient.transaction.innerClient.transactionStatus({ transactionStatusRequest: { intent_hash: id } });
718
+ const { intent_status } = result;
719
+ if (intent_status === "CommittedSuccess") return result;
720
+ if (intent_status === "CommittedFailure" || intent_status === "PermanentlyRejected") return yield* new TransactionFailedError({
721
+ status: intent_status,
722
+ statusDescription: result.intent_status_description,
723
+ message: result.error_message,
724
+ transactionId: id
725
+ });
726
+ return yield* new TransactionNotResolvedError({
727
+ status: intent_status,
728
+ statusDescription: result.intent_status_description,
729
+ message: result.error_message,
730
+ transactionId: id
731
+ });
732
+ });
733
+ return { poll: (input) => getTransactionStatus(input.id).pipe(Effect.tapErrorTag("TransactionFailedError", Effect.logError), Effect.retry({
734
+ schedule: input.retryPolicy ?? retryPolicy,
735
+ while: (error) => error._tag === "TransactionNotResolvedError"
736
+ }), Effect.timeout(input.timeout ?? pollTimeoutDuration), Effect.catchTags({ TimeoutException: () => new TimeoutError({ transactionId: input.id }) })) };
737
+ }) }) {};
738
+
739
+ //#endregion
740
+ //#region src/transactionHelper.ts
741
+ var FaucetNotAvailableError = class extends Data.TaggedError("FaucetNotAvailableError") {};
742
+ var TransactionLifeCycleHook = class extends Context.Tag("TransactionLifeCycleHook")() {};
743
+ var InsufficientXrdBalanceError = class extends Data.TaggedError("InsufficientXrdBalanceError") {};
744
+ var TransactionHelper = class extends Effect.Service()("TransactionHelper", {
745
+ dependencies: [
746
+ CreateTransactionIntent.Default,
747
+ CompileTransaction.Default,
748
+ SubmitTransaction.Default,
749
+ TransactionStatus.Default,
750
+ ManifestHelper.Default,
751
+ IntentHashService.Default,
752
+ EpochService.Default,
753
+ GetFungibleBalance.Default,
754
+ GetLedgerStateService.Default
755
+ ],
756
+ effect: Effect.gen(function* () {
757
+ const createTransactionIntent = yield* CreateTransactionIntent;
758
+ const compileTransaction = yield* CompileTransaction;
759
+ const submitTransactionToNetwork = yield* SubmitTransaction;
760
+ const transactionStatus = yield* TransactionStatus;
761
+ const signer = yield* Signer;
762
+ const manifestHelper = yield* ManifestHelper;
763
+ const intentHashService = yield* IntentHashService;
764
+ const epochService = yield* EpochService;
765
+ const gatewayApiClient = yield* GatewayApiClient;
766
+ const getLedgerStateService = yield* GetLedgerStateService;
767
+ const networkId = NetworkId.make(gatewayApiClient.networkId);
768
+ const knownAddresses = yield* Effect.tryPromise(() => RadixEngineToolkit.Utils.knownAddresses(networkId));
769
+ const getFungibleBalance = yield* GetFungibleBalance;
770
+ const lifeCycleHook = yield* Effect.serviceOption(TransactionLifeCycleHook);
771
+ const onSubmitLifeCycleHook = lifeCycleHook.pipe(Option.flatMap((item) => Option.fromNullable(item.onSubmit)));
772
+ const onSubmitSuccessLifeCycleHook = lifeCycleHook.pipe(Option.flatMap((item) => Option.fromNullable(item.onSubmitSuccess)));
773
+ const onStatusFailureLifeCycleHook = lifeCycleHook.pipe(Option.flatMap((item) => Option.fromNullable(item.onStatusFailure)));
774
+ const onSuccessLifeCycleHook = lifeCycleHook.pipe(Option.flatMap((item) => Option.fromNullable(item.onSuccess)));
775
+ const xrdBalance = (account, stateVersion) => getFungibleBalance({
776
+ addresses: [account.address],
777
+ at_ledger_state: { state_version: stateVersion }
778
+ }).pipe(Effect.map(flow(Array.head, Option.map((value) => value.items), Option.flatMap(Array.findFirst((item) => item.resource_address === knownAddresses.resourceAddresses.xrd)))));
779
+ const submitTransaction = (input) => Effect.gen(function* () {
780
+ yield* Option.fromNullable(input.feePayer).pipe(Option.match({
781
+ onNone: () => Effect.void,
782
+ onSome: (feePayer) => getLedgerStateService({ at_ledger_state: { timestamp: /* @__PURE__ */ new Date() } }).pipe(Effect.flatMap((stateVersion) => xrdBalance(feePayer.account, stateVersion.state_version)), Effect.filterOrFail((amount) => Option.match(amount, {
783
+ onNone: () => false,
784
+ onSome: (amount$1) => amount$1.amount.gte(feePayer.amount)
785
+ }), () => new InsufficientXrdBalanceError({ message: `Insufficient XRD balance for account ${feePayer.account.address}` })))
786
+ }));
787
+ const { intent, id, hash } = yield* Option.fromNullable(input.transactionIntent).pipe(Option.match({
788
+ onNone: () => Effect.gen(function* () {
789
+ yield* Effect.log("Creating transaction intent");
790
+ const feePayerInstructions = input.feePayer ? yield* manifestHelper.addFeePayer(input.feePayer) : "";
791
+ return yield* createTransactionIntent({ manifest: TransactionManifestString.make(`
792
+ ${feePayerInstructions}
793
+ ${input.manifest}
794
+ `) }).pipe(Effect.flatMap((intent$1) => intentHashService.create(intent$1).pipe(Effect.map(({ id: id$1, hash: hash$1 }) => ({
795
+ id: id$1,
796
+ hash: hash$1,
797
+ intent: intent$1
798
+ })))), Effect.catchTags({
799
+ ParseError: Effect.die,
800
+ InvalidEpochError: Effect.die
801
+ }));
802
+ }),
803
+ onSome: (intent$1) => intentHashService.create(intent$1).pipe(Effect.flatMap(({ id: id$1, hash: hash$1 }) => epochService.verifyEpochBounds({
804
+ transactionId: id$1,
805
+ transactionIntent: intent$1
806
+ }).pipe(Effect.as({
807
+ id: id$1,
808
+ hash: hash$1,
809
+ intent: intent$1
810
+ }))))
811
+ }));
812
+ return yield* Effect.gen(function* () {
813
+ yield* Effect.log("Collecting signatures");
814
+ const compiledTransaction = yield* compileTransaction({
815
+ intent,
816
+ signatures: yield* signer.signToSignatureWithPublicKey(hash)
817
+ }).pipe(Effect.catchAll(Effect.die));
818
+ yield* Option.match(onSubmitLifeCycleHook, {
819
+ onNone: () => Effect.void,
820
+ onSome: (effect) => Effect.gen(function* () {
821
+ yield* Effect.log("Executing life cycle hook: onSubmit");
822
+ yield* effect({
823
+ id,
824
+ intent
825
+ });
826
+ })
827
+ });
828
+ yield* Effect.log("Submitting transaction to network");
829
+ yield* submitTransactionToNetwork({ compiledTransaction });
830
+ yield* Option.match(onSubmitSuccessLifeCycleHook, {
831
+ onNone: () => Effect.void,
832
+ onSome: (effect) => Effect.gen(function* () {
833
+ yield* Effect.log("Executing life cycle hook: onSubmitSuccess");
834
+ yield* effect({
835
+ id,
836
+ intent
837
+ });
838
+ })
839
+ });
840
+ const result = yield* transactionStatus.poll({ id }).pipe(Effect.map((status) => ({
841
+ statusResponse: status,
842
+ id
843
+ })), Effect.catchAllCause((cause) => Effect.gen(function* () {
844
+ yield* Option.match(onStatusFailureLifeCycleHook, {
845
+ onNone: () => Effect.void,
846
+ onSome: (effect) => Effect.gen(function* () {
847
+ yield* Effect.log("Executing life cycle hook: onFailure");
848
+ yield* effect({
849
+ id,
850
+ permanent: Cause.isFailType(cause) && cause.error._tag === "TransactionFailedError",
851
+ intent
852
+ });
853
+ })
854
+ });
855
+ return yield* Effect.failCause(cause);
856
+ })));
857
+ yield* Option.match(onSuccessLifeCycleHook, {
858
+ onNone: () => Effect.void,
859
+ onSome: (effect) => Effect.gen(function* () {
860
+ yield* Effect.log("Executing life cycle hook: onSuccess");
861
+ yield* effect({ id });
862
+ })
863
+ });
864
+ return result;
865
+ }).pipe(Effect.annotateLogs("transactionId", id));
866
+ }).pipe(Effect.annotateLogs({
867
+ networkId,
868
+ feePayer: input.feePayer?.account.address
869
+ }));
870
+ const getCommittedDetails = (input) => gatewayApiClient.transaction.getCommittedDetails(input.id);
871
+ const createBadge$1 = (input) => submitTransaction({
872
+ manifest: createBadge(input.account, input.initialSupply),
873
+ feePayer: {
874
+ account: input.feePayer,
875
+ amount: Amount.make("10")
876
+ }
877
+ }).pipe(Effect.flatMap(({ id }) => getCommittedDetails({ id })), Effect.map((result) => pipe(Option.fromNullable(result.transaction.balance_changes?.fungible_balance_changes), Option.flatMap(Array.head), Option.flatMap(Record.get("resource_address")), Option.getOrThrow, FungibleResourceAddress.make)));
878
+ const createFungibleToken = (input) => submitTransaction({
879
+ manifest: createFungibleTokenManifest(input),
880
+ feePayer: {
881
+ account: input.feePayer,
882
+ amount: Amount.make("10")
883
+ }
884
+ }).pipe(Effect.flatMap(({ id }) => getCommittedDetails({ id })), Effect.map((result) => pipe(Option.fromNullable(result.transaction.balance_changes?.fungible_balance_changes), Option.flatMap(Array.head), Option.flatMap(Record.get("resource_address")), Option.getOrThrow, FungibleResourceAddress.make)));
885
+ const faucet$1 = (input) => Effect.gen(function* () {
886
+ if (networkId === 1) return yield* new FaucetNotAvailableError({ message: "Faucet is only available on Testnet" }).pipe(Effect.die);
887
+ return yield* submitTransaction({ manifest: yield* faucet(input.account.address) });
888
+ });
889
+ return {
890
+ submitTransaction,
891
+ getCommittedDetails,
892
+ createBadge: createBadge$1,
893
+ createFungibleToken,
894
+ faucet: faucet$1
895
+ };
896
+ })
897
+ }) {};
898
+
899
+ //#endregion
900
+ export { CompileTransaction, CreateTransactionIntent, DisableTestClock, EpochService, FailedToCompileTransactionError, FailedToCreateIntentHashError, FailedToNotarizeTransactionError, FailedToSignTransactionError, FailedToStaticallyValidateManifestError, FaucetNotAvailableError, InsufficientXrdBalanceError, IntentHashService, InvalidEndEpochError, InvalidEpochError, InvalidManifestError, InvalidStartEpochError, ManifestHelper, NotaryKeyPair, PreviewTransaction, Signer, StaticallyValidateManifest, SubmitTransaction, TimeoutError, TransactionFailedError, TransactionHeader, TransactionHelper, TransactionLifeCycleHook, TransactionNotResolvedError, TransactionStatus, Vault, addFeePayer, createAccount, createBadge, createFungibleTokenManifest, faucet, schemas_exports as schemas };