@midnight-ntwrk/wallet-sdk-shielded 1.0.0-beta.10

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.
Files changed (41) hide show
  1. package/dist/ShieldedWallet.d.ts +70 -0
  2. package/dist/ShieldedWallet.js +136 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +13 -0
  5. package/dist/v1/CoinsAndBalances.d.ts +24 -0
  6. package/dist/v1/CoinsAndBalances.js +46 -0
  7. package/dist/v1/CoreWallet.d.ts +49 -0
  8. package/dist/v1/CoreWallet.js +152 -0
  9. package/dist/v1/Keys.d.ts +8 -0
  10. package/dist/v1/Keys.js +16 -0
  11. package/dist/v1/Proving.d.ts +12 -0
  12. package/dist/v1/Proving.js +53 -0
  13. package/dist/v1/ProvingRecipe.d.ts +18 -0
  14. package/dist/v1/ProvingRecipe.js +3 -0
  15. package/dist/v1/RunningV1Variant.d.ts +46 -0
  16. package/dist/v1/RunningV1Variant.js +95 -0
  17. package/dist/v1/Serialization.d.ts +12 -0
  18. package/dist/v1/Serialization.js +103 -0
  19. package/dist/v1/Simulator.d.ts +22 -0
  20. package/dist/v1/Simulator.js +102 -0
  21. package/dist/v1/Submission.d.ts +31 -0
  22. package/dist/v1/Submission.js +69 -0
  23. package/dist/v1/Sync.d.ts +51 -0
  24. package/dist/v1/Sync.js +139 -0
  25. package/dist/v1/SyncProgress.d.ts +22 -0
  26. package/dist/v1/SyncProgress.js +25 -0
  27. package/dist/v1/Transacting.d.ts +69 -0
  28. package/dist/v1/Transacting.js +374 -0
  29. package/dist/v1/Transaction.d.ts +17 -0
  30. package/dist/v1/Transaction.js +76 -0
  31. package/dist/v1/TransactionHistory.d.ts +16 -0
  32. package/dist/v1/TransactionHistory.js +55 -0
  33. package/dist/v1/TransactionImbalances.d.ts +10 -0
  34. package/dist/v1/TransactionImbalances.js +30 -0
  35. package/dist/v1/V1Builder.d.ts +109 -0
  36. package/dist/v1/V1Builder.js +201 -0
  37. package/dist/v1/WalletError.d.ts +75 -0
  38. package/dist/v1/WalletError.js +59 -0
  39. package/dist/v1/index.d.ts +16 -0
  40. package/dist/v1/index.js +28 -0
  41. package/package.json +77 -0
@@ -0,0 +1,46 @@
1
+ import * as ledger from '@midnight-ntwrk/ledger-v6';
2
+ import { Effect, Scope, Stream } from 'effect';
3
+ import { WalletRuntimeError, Variant, StateChange } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
4
+ import { ProvingService } from './Proving.js';
5
+ import { ProvingRecipe } from './ProvingRecipe.js';
6
+ import { SerializationCapability } from './Serialization.js';
7
+ import { EventsSyncUpdate, SyncCapability, SyncService } from './Sync.js';
8
+ import { TransactingCapability, TokenTransfer } from './Transacting.js';
9
+ import { WalletError } from './WalletError.js';
10
+ import { CoinsAndBalancesCapability } from './CoinsAndBalances.js';
11
+ import { KeysCapability } from './Keys.js';
12
+ import { SubmissionService, SubmitTransactionMethod } from './Submission.js';
13
+ import { CoinSelection } from '@midnight-ntwrk/wallet-sdk-capabilities';
14
+ import { CoreWallet } from './CoreWallet.js';
15
+ import { TransactionHistoryCapability } from './TransactionHistory.js';
16
+ export declare namespace RunningV1Variant {
17
+ type Context<TSerialized, TSyncUpdate, TTransaction, TStartAux> = {
18
+ serializationCapability: SerializationCapability<CoreWallet, null, TSerialized>;
19
+ syncService: SyncService<CoreWallet, TStartAux, TSyncUpdate>;
20
+ syncCapability: SyncCapability<CoreWallet, TSyncUpdate>;
21
+ transactingCapability: TransactingCapability<ledger.ZswapSecretKeys, CoreWallet, TTransaction>;
22
+ provingService: ProvingService<TTransaction>;
23
+ coinsAndBalancesCapability: CoinsAndBalancesCapability<CoreWallet>;
24
+ keysCapability: KeysCapability<CoreWallet>;
25
+ submissionService: SubmissionService<TTransaction>;
26
+ coinSelection: CoinSelection<ledger.QualifiedShieldedCoinInfo>;
27
+ transactionHistoryCapability: TransactionHistoryCapability<CoreWallet, TTransaction>;
28
+ };
29
+ type AnyContext = Context<any, any, any, any>;
30
+ }
31
+ export declare const V1Tag: unique symbol;
32
+ export type DefaultRunningV1 = RunningV1Variant<string, EventsSyncUpdate, ledger.FinalizedTransaction, ledger.ZswapSecretKeys>;
33
+ export declare class RunningV1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux> implements Variant.RunningVariant<typeof V1Tag, CoreWallet> {
34
+ #private;
35
+ readonly __polyTag__: typeof V1Tag;
36
+ readonly state: Stream.Stream<StateChange.StateChange<CoreWallet>, WalletRuntimeError>;
37
+ constructor(scope: Scope.Scope, context: Variant.VariantContext<CoreWallet>, v1Context: RunningV1Variant.Context<TSerialized, TSyncUpdate, TTransaction, TStartAux>);
38
+ startSyncInBackground(startAux: TStartAux): Effect.Effect<void>;
39
+ startSync(startAux: TStartAux): Stream.Stream<void, WalletError, Scope.Scope>;
40
+ balanceTransaction(secretKeys: ledger.ZswapSecretKeys, tx: ledger.Transaction<ledger.Signaturish, ledger.Proofish, ledger.Bindingish>): Effect.Effect<ProvingRecipe<TTransaction>, WalletError>;
41
+ transferTransaction(secretKeys: ledger.ZswapSecretKeys, outputs: ReadonlyArray<TokenTransfer>): Effect.Effect<ProvingRecipe<TTransaction>, WalletError>;
42
+ initSwap(secretKeys: ledger.ZswapSecretKeys, desiredInputs: Record<ledger.RawTokenType, bigint>, desiredOutputs: ReadonlyArray<TokenTransfer>): Effect.Effect<ProvingRecipe<TTransaction>, WalletError>;
43
+ finalizeTransaction(recipe: ProvingRecipe<TTransaction>): Effect.Effect<TTransaction, WalletError>;
44
+ submitTransaction: SubmitTransactionMethod<TTransaction>;
45
+ serializeState(state: CoreWallet): TSerialized;
46
+ }
@@ -0,0 +1,95 @@
1
+ import { Effect, pipe, Scope, Stream, SubscriptionRef, Schedule, Duration, Sink, Console } from 'effect';
2
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
3
+ import { StateChange, VersionChangeType, } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
4
+ import { EitherOps } from '@midnight-ntwrk/wallet-sdk-utilities';
5
+ import { OtherWalletError } from './WalletError.js';
6
+ const progress = (state) => {
7
+ const appliedIndex = state.progress?.appliedIndex ?? 0n;
8
+ const highestRelevantWalletIndex = state.progress?.highestRelevantWalletIndex ?? 0n;
9
+ const highestIndex = state.progress?.highestIndex ?? 0n;
10
+ const highestRelevantIndex = state.progress?.highestRelevantIndex ?? 0n;
11
+ const sourceGap = highestIndex - highestRelevantIndex;
12
+ const applyGap = highestRelevantWalletIndex - appliedIndex;
13
+ return [StateChange.ProgressUpdate({ sourceGap, applyGap })];
14
+ };
15
+ const protocolVersionChange = (previous, current) => {
16
+ return previous.protocolVersion != current.protocolVersion
17
+ ? [
18
+ StateChange.VersionChange({
19
+ change: VersionChangeType.Version({
20
+ version: ProtocolVersion.ProtocolVersion(current.protocolVersion),
21
+ }),
22
+ }),
23
+ ]
24
+ : [];
25
+ };
26
+ export const V1Tag = Symbol('V1');
27
+ export class RunningV1Variant {
28
+ __polyTag__ = V1Tag;
29
+ #scope;
30
+ #context;
31
+ #v1Context;
32
+ state;
33
+ constructor(scope, context, v1Context) {
34
+ this.#scope = scope;
35
+ this.#context = context;
36
+ this.#v1Context = v1Context;
37
+ this.state = Stream.fromEffect(context.stateRef.get).pipe(Stream.flatMap((initialState) => context.stateRef.changes.pipe(Stream.mapAccum(initialState, (previous, current) => {
38
+ return [current, [previous, current]];
39
+ }))), Stream.mapConcat(([previous, current]) => {
40
+ // TODO: emit progress only upon actual change
41
+ return [
42
+ StateChange.State({ state: current }),
43
+ ...progress(current),
44
+ ...protocolVersionChange(previous, current),
45
+ ];
46
+ }));
47
+ }
48
+ startSyncInBackground(startAux) {
49
+ return this.startSync(startAux).pipe(Stream.runScoped(Sink.drain), Effect.forkScoped, Effect.provideService(Scope.Scope, this.#scope));
50
+ }
51
+ startSync(startAux) {
52
+ return pipe(SubscriptionRef.get(this.#context.stateRef), Stream.fromEffect, Stream.flatMap((state) => this.#v1Context.syncService.updates(state, startAux)), Stream.mapEffect((update) => {
53
+ return SubscriptionRef.updateEffect(this.#context.stateRef, (state) => Effect.try({
54
+ try: () => this.#v1Context.syncCapability.applyUpdate(state, update),
55
+ catch: (err) => new OtherWalletError({
56
+ message: 'Error while applying sync update',
57
+ cause: err,
58
+ }),
59
+ }));
60
+ }), Stream.tapError((error) => Console.error(error)), Stream.retry(pipe(Schedule.exponential(Duration.seconds(1), 2), Schedule.map((delay) => {
61
+ const maxDelay = Duration.minutes(2);
62
+ const jitter = Duration.millis(Math.floor(Math.random() * 1000));
63
+ const delayWithJitter = Duration.toMillis(delay) + Duration.toMillis(jitter);
64
+ return Duration.millis(Math.min(delayWithJitter, Duration.toMillis(maxDelay)));
65
+ }))));
66
+ }
67
+ balanceTransaction(secretKeys, tx) {
68
+ return SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => {
69
+ return pipe(this.#v1Context.transactingCapability.balanceTransaction(secretKeys, state, tx), EitherOps.toEffect, Effect.map(({ recipe, newState }) => [recipe, newState]));
70
+ });
71
+ }
72
+ transferTransaction(secretKeys, outputs) {
73
+ return SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => {
74
+ return pipe(this.#v1Context.transactingCapability.makeTransfer(secretKeys, state, outputs), EitherOps.toEffect, Effect.map(({ recipe, newState }) => [recipe, newState]));
75
+ });
76
+ }
77
+ initSwap(secretKeys, desiredInputs, desiredOutputs) {
78
+ return SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => {
79
+ return pipe(this.#v1Context.transactingCapability.initSwap(secretKeys, state, desiredInputs, desiredOutputs), EitherOps.toEffect, Effect.map(({ recipe, newState }) => [recipe, newState]));
80
+ });
81
+ }
82
+ finalizeTransaction(recipe) {
83
+ return this.#v1Context.provingService
84
+ .prove(recipe)
85
+ .pipe(Effect.tapError(() => SubscriptionRef.updateEffect(this.#context.stateRef, (state) => EitherOps.toEffect(this.#v1Context.transactingCapability.revertRecipe(state, recipe)))));
86
+ }
87
+ submitTransaction = ((transaction, waitForStatus = 'InBlock') => {
88
+ return this.#v1Context.submissionService
89
+ .submitTransaction(transaction, waitForStatus)
90
+ .pipe(Effect.tapError(() => SubscriptionRef.updateEffect(this.#context.stateRef, (state) => EitherOps.toEffect(this.#v1Context.transactingCapability.revert(state, transaction)))));
91
+ });
92
+ serializeState(state) {
93
+ return this.#v1Context.serializationCapability.serialize(state);
94
+ }
95
+ }
@@ -0,0 +1,12 @@
1
+ import { Either } from 'effect';
2
+ import { WalletError } from './WalletError.js';
3
+ import { CoreWallet } from './CoreWallet.js';
4
+ import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
5
+ export type SerializationCapability<TWallet, TAux, TSerialized> = {
6
+ serialize(wallet: TWallet): TSerialized;
7
+ deserialize(aux: TAux, data: TSerialized): Either.Either<TWallet, WalletError>;
8
+ };
9
+ export type DefaultSerializationConfiguration = {
10
+ networkId: NetworkId.NetworkId;
11
+ };
12
+ export declare const makeDefaultV1SerializationCapability: () => SerializationCapability<CoreWallet, null, string>;
@@ -0,0 +1,103 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) 2025 Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { Effect, ParseResult, Either, pipe, Schema } from 'effect';
14
+ import { WalletError } from './WalletError.js';
15
+ import * as ledger from '@midnight-ntwrk/ledger-v6';
16
+ import { CoreWallet } from './CoreWallet.js';
17
+ const TxSchema = Schema.declare((input) => input instanceof ledger.Transaction).annotations({
18
+ identifier: 'ledger.Transaction',
19
+ });
20
+ const StateSchema = Schema.declare((input) => input instanceof ledger.ZswapLocalState).annotations({
21
+ identifier: 'ledger.ZswapLocalState',
22
+ });
23
+ const Uint8ArraySchema = Schema.declare((input) => input instanceof Uint8Array).annotations({
24
+ identifier: 'Uint8Array',
25
+ });
26
+ const TxFromUint8Array = () => Schema.asSchema(Schema.transformOrFail(Uint8ArraySchema, TxSchema, {
27
+ encode: (tx) => {
28
+ return Effect.try({
29
+ try: () => {
30
+ return tx.serialize();
31
+ },
32
+ catch: (err) => {
33
+ return new ParseResult.Unexpected(err, 'Could not serialize transaction');
34
+ },
35
+ });
36
+ },
37
+ decode: (bytes) => Effect.try({
38
+ try: () => ledger.Transaction.deserialize('signature', 'proof', 'binding', bytes),
39
+ catch: (err) => {
40
+ return new ParseResult.Unexpected(err, 'Could not deserialize transaction');
41
+ },
42
+ }),
43
+ }));
44
+ const StateFromUInt8Array = () => Schema.asSchema(Schema.transformOrFail(Uint8ArraySchema, StateSchema, {
45
+ encode: (state) => {
46
+ return Effect.try({
47
+ try: () => {
48
+ return state.serialize();
49
+ },
50
+ catch: (err) => {
51
+ return new ParseResult.Unexpected(err, 'Could not serialize local state');
52
+ },
53
+ });
54
+ },
55
+ decode: (bytes) => Effect.try({
56
+ try: () => ledger.ZswapLocalState.deserialize(bytes),
57
+ catch: (err) => {
58
+ return new ParseResult.Unexpected(err, 'Could not deserialize local state');
59
+ },
60
+ }),
61
+ }));
62
+ const HexedTx = () => pipe(Schema.Uint8ArrayFromHex, Schema.compose(TxFromUint8Array()));
63
+ const HexedState = () => pipe(Schema.Uint8ArrayFromHex, Schema.compose(StateFromUInt8Array()));
64
+ export const makeDefaultV1SerializationCapability = () => {
65
+ const SnapshotSchema = Schema.Struct({
66
+ publicKeys: Schema.Struct({
67
+ coinPublicKey: Schema.String,
68
+ encryptionPublicKey: Schema.String,
69
+ }),
70
+ txHistory: Schema.Array(HexedTx()),
71
+ state: HexedState(),
72
+ protocolVersion: Schema.BigInt,
73
+ offset: Schema.optional(Schema.BigInt),
74
+ networkId: Schema.String,
75
+ coinHashes: Schema.Record({
76
+ key: Schema.String,
77
+ value: Schema.Struct({ nullifier: Schema.String, commitment: Schema.String }),
78
+ }),
79
+ });
80
+ return {
81
+ serialize: (wallet) => {
82
+ const buildSnapshot = (w) => ({
83
+ publicKeys: w.publicKeys,
84
+ txHistory: w.txHistoryArray,
85
+ state: w.state,
86
+ protocolVersion: w.protocolVersion,
87
+ networkId: w.networkId,
88
+ offset: w.progress?.appliedIndex,
89
+ coinHashes: w.coinHashes,
90
+ });
91
+ return pipe(wallet, buildSnapshot, Schema.encodeSync(SnapshotSchema), JSON.stringify);
92
+ },
93
+ deserialize: (aux, serialized) => {
94
+ return pipe(serialized, Schema.decodeUnknownEither(Schema.parseJson(SnapshotSchema)), Either.mapLeft((err) => WalletError.other(err)), Either.flatMap((snapshot) => CoreWallet.restoreWithCoinHashes(snapshot.publicKeys, snapshot.state, snapshot.txHistory, snapshot.coinHashes, {
95
+ appliedIndex: snapshot.offset ?? 0n,
96
+ highestRelevantWalletIndex: 0n,
97
+ highestIndex: 0n,
98
+ highestRelevantIndex: 0n,
99
+ isConnected: false,
100
+ }, snapshot.protocolVersion, snapshot.networkId)));
101
+ },
102
+ };
103
+ };
@@ -0,0 +1,22 @@
1
+ import * as ledger from '@midnight-ntwrk/ledger-v6';
2
+ import { Array as Arr, Effect, Scope, Stream, SubscriptionRef } from 'effect';
3
+ export type SimulatorState = Readonly<{
4
+ ledger: ledger.LedgerState;
5
+ lastTxResult: ledger.TransactionResult;
6
+ lastTxNumber: bigint;
7
+ }>;
8
+ export declare class Simulator {
9
+ #private;
10
+ static nextBlockContext: (number: bigint) => Effect.Effect<ledger.BlockContext>;
11
+ static init(genesisMints: Readonly<Arr.NonEmptyArray<{
12
+ amount: bigint;
13
+ type: ledger.RawTokenType;
14
+ recipient: ledger.ZswapSecretKeys;
15
+ }>>): Effect.Effect<Simulator, never, Scope.Scope>;
16
+ readonly state$: Stream.Stream<SimulatorState>;
17
+ constructor(stateRef: SubscriptionRef.SubscriptionRef<SimulatorState>, state$: Stream.Stream<SimulatorState>);
18
+ submitRegularTx(tx: ledger.ProofErasedTransaction): Effect.Effect<{
19
+ blockNumber: bigint;
20
+ blockHash: string;
21
+ }>;
22
+ }
@@ -0,0 +1,102 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) 2025 Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import * as ledger from '@midnight-ntwrk/ledger-v6';
14
+ import { Array as Arr, Effect, Encoding, pipe, Stream, SubscriptionRef, Clock } from 'effect';
15
+ import { ArrayOps, EitherOps } from '@midnight-ntwrk/wallet-sdk-utilities';
16
+ import * as crypto from 'crypto';
17
+ import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
18
+ const simpleHash = (input) => {
19
+ return Encoding.decodeHex(input).pipe(EitherOps.toEffect, Effect.andThen((parsed) => Effect.promise(() => crypto.subtle.digest('SHA-256', parsed))), Effect.andThen((out) => Encoding.encodeHex(new Uint8Array(out))), Effect.orDie);
20
+ };
21
+ const wellFormedStrictness = (params = {}) => {
22
+ const strictness = new ledger.WellFormedStrictness();
23
+ // Note: Enforce balancing should be true by default outside genesis mints
24
+ strictness.enforceBalancing = params?.enforceBalancing ?? false;
25
+ strictness.verifyNativeProofs = params?.verifyNativeProofs ?? false;
26
+ strictness.verifyContractProofs = params?.verifyContractProofs ?? false;
27
+ strictness.enforceLimits = params?.enforceLimits ?? false;
28
+ strictness.verifySignatures = params?.verifySignatures ?? false;
29
+ return strictness;
30
+ };
31
+ export class Simulator {
32
+ static nextBlockContext = (number) => pipe(number.toString(16), (str) => (str.length % 2 == 0 ? str : str.padStart(str.length + 1, '0')), simpleHash, Effect.map((hash) => ({
33
+ parentBlockHash: hash,
34
+ secondsSinceEpoch: number,
35
+ secondsSinceEpochErr: 1,
36
+ })));
37
+ static init(genesisMints) {
38
+ const emptyState = ledger.LedgerState.blank(NetworkId.NetworkId.Undeployed);
39
+ const noStrictness = wellFormedStrictness();
40
+ const makeTransactions = (context) => Effect.gen(function* () {
41
+ const nowMillis = yield* Clock.currentTimeMillis;
42
+ const verificationTime = new Date(nowMillis);
43
+ const tx = pipe(genesisMints, Arr.map((transfer) => {
44
+ const coin = ledger.createShieldedCoinInfo(transfer.type, transfer.amount);
45
+ const output = ledger.ZswapOutput.new(coin, 0, transfer.recipient.coinPublicKey, transfer.recipient.encryptionPublicKey);
46
+ return ledger.ZswapOffer.fromOutput(output, transfer.type, transfer.amount);
47
+ }), ArrayOps.fold((acc, offer) => acc.merge(offer)), (offer) => ledger.Transaction.fromParts(NetworkId.NetworkId.Undeployed, offer).eraseProofs(), (tx) => tx.wellFormed(emptyState, noStrictness, verificationTime));
48
+ const [initialState, initialResult] = emptyState.apply(tx, new ledger.TransactionContext(emptyState, context));
49
+ const postBlockUpdateState = initialState.postBlockUpdate(verificationTime);
50
+ return {
51
+ initialResult,
52
+ initialState: postBlockUpdateState,
53
+ tx,
54
+ };
55
+ });
56
+ return Effect.gen(function* () {
57
+ const context = yield* Simulator.nextBlockContext(0n);
58
+ const init = yield* makeTransactions(context);
59
+ const initialState = {
60
+ ledger: init.initialState,
61
+ lastTxResult: init.initialResult,
62
+ lastTxNumber: 0n,
63
+ };
64
+ const ref = yield* SubscriptionRef.make(initialState);
65
+ const changesStream = yield* Stream.share(ref.changes, {
66
+ capacity: 'unbounded',
67
+ replay: Number.MAX_SAFE_INTEGER,
68
+ });
69
+ yield* pipe(changesStream, Stream.runDrain, Effect.forkScoped);
70
+ return new Simulator(ref, changesStream);
71
+ });
72
+ }
73
+ #stateRef;
74
+ state$;
75
+ constructor(stateRef, state$) {
76
+ this.#stateRef = stateRef;
77
+ this.state$ = state$;
78
+ }
79
+ submitRegularTx(tx) {
80
+ return pipe(this.#stateRef, SubscriptionRef.modifyEffect((simulatorState) => Effect.gen(function* () {
81
+ const nextNumber = simulatorState.lastTxNumber + 1n;
82
+ const context = yield* Simulator.nextBlockContext(nextNumber);
83
+ const nowMillis = yield* Clock.currentTimeMillis;
84
+ const verificationTime = new Date(nowMillis);
85
+ const noStrictness = wellFormedStrictness();
86
+ const verifiedTx = tx.wellFormed(simulatorState.ledger, noStrictness, verificationTime);
87
+ const [newState, result] = simulatorState.ledger.apply(verifiedTx, new ledger.TransactionContext(simulatorState.ledger, context));
88
+ const postBlockUpdatedState = newState.postBlockUpdate(verificationTime);
89
+ const newSimulatorState = {
90
+ ...simulatorState,
91
+ ledger: postBlockUpdatedState,
92
+ lastTxResult: result,
93
+ lastTxNumber: nextNumber,
94
+ };
95
+ const output = {
96
+ blockNumber: nextNumber,
97
+ blockHash: context.parentBlockHash,
98
+ };
99
+ return [output, newSimulatorState];
100
+ })));
101
+ }
102
+ }
@@ -0,0 +1,31 @@
1
+ import { Effect } from 'effect';
2
+ import * as ledger from '@midnight-ntwrk/ledger-v6';
3
+ import { WalletError } from './WalletError.js';
4
+ import { Simulator } from './Simulator.js';
5
+ import { SubmissionEvent as SubmissionEventImported } from '@midnight-ntwrk/wallet-sdk-node-client/effect';
6
+ export declare const SubmissionEvent: typeof SubmissionEventImported;
7
+ export type SubmissionEvent = SubmissionEventImported.SubmissionEvent;
8
+ export declare namespace SubmissionEventCases {
9
+ type Finalized = SubmissionEventImported.Cases.Finalized;
10
+ type Submitted = SubmissionEventImported.Cases.Submitted;
11
+ type InBlock = SubmissionEventImported.Cases.InBlock;
12
+ }
13
+ export type SubmitTransactionMethod<TTransaction> = {
14
+ (transaction: TTransaction, waitForStatus: 'Submitted'): Effect.Effect<SubmissionEventCases.Submitted, WalletError>;
15
+ (transaction: TTransaction, waitForStatus: 'InBlock'): Effect.Effect<SubmissionEventCases.InBlock, WalletError>;
16
+ (transaction: TTransaction, waitForStatus: 'Finalized'): Effect.Effect<SubmissionEventCases.Finalized, WalletError>;
17
+ (transaction: TTransaction): Effect.Effect<SubmissionEventCases.InBlock, WalletError>;
18
+ (transaction: TTransaction, waitForStatus?: 'Submitted' | 'InBlock' | 'Finalized'): Effect.Effect<SubmissionEvent, WalletError>;
19
+ };
20
+ export interface SubmissionService<TTransaction> {
21
+ submitTransaction: SubmitTransactionMethod<TTransaction>;
22
+ close(): Effect.Effect<void>;
23
+ }
24
+ export type DefaultSubmissionConfiguration = {
25
+ relayURL: URL;
26
+ };
27
+ export declare const makeDefaultSubmissionService: (config: DefaultSubmissionConfiguration) => SubmissionService<ledger.FinalizedTransaction>;
28
+ export type SimulatorSubmissionConfiguration = {
29
+ simulator: Simulator;
30
+ };
31
+ export declare const makeSimulatorSubmissionService: (waitForStatus?: "Submitted" | "InBlock" | "Finalized") => (config: SimulatorSubmissionConfiguration) => SubmissionService<ledger.ProofErasedTransaction>;
@@ -0,0 +1,69 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) 2025 Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { Deferred, Effect, Encoding, Exit, pipe, Scope } from 'effect';
14
+ import { WalletError } from './WalletError.js';
15
+ import { NodeClient, PolkadotNodeClient, SubmissionEvent as SubmissionEventImported, } from '@midnight-ntwrk/wallet-sdk-node-client/effect';
16
+ export const SubmissionEvent = SubmissionEventImported;
17
+ export const makeDefaultSubmissionService = (config) => {
18
+ const scopeAndClientDeferred = Deferred.make().pipe(Effect.runSync);
19
+ const makeScopeAndClient = Effect.gen(function* () {
20
+ const scope = yield* Scope.make();
21
+ const client = yield* PolkadotNodeClient.make({
22
+ nodeURL: config.relayURL,
23
+ }).pipe(Effect.provideService(Scope.Scope, scope));
24
+ return { scope, client };
25
+ });
26
+ void pipe(scopeAndClientDeferred, Deferred.complete(makeScopeAndClient), Effect.runPromise);
27
+ const submit = (transaction, waitForStatus = 'InBlock') => {
28
+ return pipe(NodeClient.sendMidnightTransactionAndWait(transaction.serialize(), waitForStatus), Effect.provideServiceEffect(NodeClient.NodeClient, pipe(scopeAndClientDeferred, Deferred.await, Effect.map(({ client }) => client))), Effect.mapError((err) => WalletError.submission(err)));
29
+ };
30
+ return {
31
+ submitTransaction: submit,
32
+ close() {
33
+ return pipe(scopeAndClientDeferred, Deferred.await, Effect.flatMap(({ scope }) => Scope.close(scope, Exit.void)), Effect.ignoreLogged);
34
+ },
35
+ };
36
+ };
37
+ export const makeSimulatorSubmissionService = (waitForStatus = 'InBlock') => (config) => {
38
+ const submit = (transaction) => {
39
+ const serializedTx = transaction.serialize();
40
+ return config.simulator.submitRegularTx(transaction).pipe(Effect.map((output) => {
41
+ // Let's mimic node's client behavior here
42
+ switch (waitForStatus) {
43
+ case 'Submitted':
44
+ return SubmissionEvent.Submitted({
45
+ tx: serializedTx,
46
+ txHash: Encoding.encodeHex(serializedTx.subarray(0, 32)),
47
+ });
48
+ case 'InBlock':
49
+ return SubmissionEvent.InBlock({
50
+ tx: serializedTx,
51
+ blockHash: output.blockHash,
52
+ blockHeight: output.blockNumber,
53
+ txHash: Encoding.encodeHex(serializedTx.subarray(0, 32)),
54
+ });
55
+ case 'Finalized':
56
+ return SubmissionEvent.Finalized({
57
+ tx: serializedTx,
58
+ blockHash: output.blockHash,
59
+ blockHeight: output.blockNumber,
60
+ txHash: Encoding.encodeHex(serializedTx.subarray(0, 32)),
61
+ });
62
+ }
63
+ }));
64
+ };
65
+ return {
66
+ submitTransaction: submit,
67
+ close: () => Effect.void,
68
+ };
69
+ };
@@ -0,0 +1,51 @@
1
+ import * as ledger from '@midnight-ntwrk/ledger-v6';
2
+ import { Scope, Stream, Schema } from 'effect';
3
+ import { CoreWallet } from './CoreWallet.js';
4
+ import { Simulator, SimulatorState } from './Simulator.js';
5
+ import { WalletError } from './WalletError.js';
6
+ import { TransactionHistoryCapability } from './TransactionHistory.js';
7
+ export interface SyncService<TState, TStartAux, TUpdate> {
8
+ updates: (state: TState, auxData: TStartAux) => Stream.Stream<TUpdate, WalletError, Scope.Scope>;
9
+ }
10
+ export interface SyncCapability<TState, TUpdate> {
11
+ applyUpdate: (state: TState, update: TUpdate) => TState;
12
+ }
13
+ export type IndexerClientConnection = {
14
+ indexerHttpUrl: string;
15
+ indexerWsUrl?: string;
16
+ };
17
+ export type DefaultSyncConfiguration = {
18
+ indexerClientConnection: IndexerClientConnection;
19
+ };
20
+ export type DefaultSyncContext = {
21
+ transactionHistoryCapability: TransactionHistoryCapability<CoreWallet, ledger.FinalizedTransaction>;
22
+ };
23
+ type SecretKeysResource = <A>(cb: (keys: ledger.ZswapSecretKeys) => A) => A;
24
+ export declare const SecretKeysResource: {
25
+ create: (secretKeys: ledger.ZswapSecretKeys) => SecretKeysResource;
26
+ };
27
+ export type WalletSyncUpdate = {
28
+ update: EventsSyncUpdate;
29
+ secretKeys: SecretKeysResource;
30
+ };
31
+ export declare const WalletSyncUpdate: {
32
+ create: (update: EventsSyncUpdate, secretKeys: ledger.ZswapSecretKeys) => WalletSyncUpdate;
33
+ };
34
+ export declare const EventsSyncUpdate: Schema.TaggedStruct<"EventsSyncUpdate", {
35
+ id: typeof Schema.Number;
36
+ maxId: typeof Schema.Number;
37
+ event: Schema.declare<ledger.Event, ledger.Event, readonly [], never>;
38
+ }>;
39
+ export type EventsSyncUpdate = Schema.Schema.Type<typeof EventsSyncUpdate>;
40
+ export declare const makeEventsSyncService: (config: DefaultSyncConfiguration) => SyncService<CoreWallet, ledger.ZswapSecretKeys, WalletSyncUpdate>;
41
+ export declare const makeEventsSyncCapability: () => SyncCapability<CoreWallet, WalletSyncUpdate>;
42
+ export type SimulatorSyncConfiguration = {
43
+ simulator: Simulator;
44
+ };
45
+ export type SimulatorSyncUpdate = {
46
+ update: SimulatorState;
47
+ secretKeys: ledger.ZswapSecretKeys;
48
+ };
49
+ export declare const makeSimulatorSyncService: (config: SimulatorSyncConfiguration) => SyncService<CoreWallet, ledger.ZswapSecretKeys, SimulatorSyncUpdate>;
50
+ export declare const makeSimulatorSyncCapability: () => SyncCapability<CoreWallet, SimulatorSyncUpdate>;
51
+ export {};