@midnight-ntwrk/wallet-sdk-dust-wallet 1.0.0-beta.8 → 1.0.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.
@@ -1,3 +1,15 @@
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.
1
13
  import { Deferred, Effect, Encoding, Exit, pipe, Scope } from 'effect';
2
14
  import { NodeClient, PolkadotNodeClient, SubmissionEvent as SubmissionEventImported, } from '@midnight-ntwrk/wallet-sdk-node-client/effect';
3
15
  import { WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
package/dist/Sync.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Effect, Layer, Schema, Scope, Stream } from 'effect';
2
- import { DustSecretKey, Event as LedgerEvent, LedgerParameters } from '@midnight-ntwrk/ledger-v6';
2
+ import { DustSecretKey, Event as LedgerEvent, LedgerParameters } from '@midnight-ntwrk/ledger-v7';
3
3
  import { SubscriptionClient, QueryClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
4
4
  import { WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
5
5
  import { Simulator, SimulatorState } from './Simulator.js';
@@ -7,7 +7,13 @@ import { DustCoreWallet } from './DustCoreWallet.js';
7
7
  import { NetworkId } from './types/ledger.js';
8
8
  export interface SyncService<TState, TStartAux, TUpdate> {
9
9
  updates: (state: TState, auxData: TStartAux) => Stream.Stream<TUpdate, WalletError.WalletError, Scope.Scope>;
10
- ledgerParameters: () => Effect.Effect<LedgerParameters, WalletError.WalletError>;
10
+ blockData: () => Effect.Effect<BlockData, WalletError.WalletError>;
11
+ }
12
+ export interface BlockData {
13
+ hash: string;
14
+ height: number;
15
+ ledgerParameters: LedgerParameters;
16
+ timestamp: Date;
11
17
  }
12
18
  export interface SyncCapability<TState, TUpdate> {
13
19
  applyUpdate: (state: TState, update: TUpdate) => TState;
@@ -39,11 +45,12 @@ export declare const SyncEventsUpdateSchema: Schema.Struct<{
39
45
  }>;
40
46
  export type WalletSyncSubscription = Schema.Schema.Type<typeof SyncEventsUpdateSchema>;
41
47
  export type WalletSyncUpdate = {
42
- update: WalletSyncSubscription;
48
+ updates: WalletSyncSubscription[];
43
49
  secretKeys: SecretKeysResource;
50
+ timestamp: Date;
44
51
  };
45
52
  export declare const WalletSyncUpdate: {
46
- create: (update: WalletSyncSubscription, secretKey: DustSecretKey) => WalletSyncUpdate;
53
+ create: (updates: WalletSyncSubscription[], secretKey: DustSecretKey, timestamp: Date) => WalletSyncUpdate;
47
54
  };
48
55
  export declare const makeDefaultSyncService: (config: DefaultSyncConfiguration) => SyncService<DustCoreWallet, DustSecretKey, WalletSyncUpdate>;
49
56
  export type IndexerSyncService = {
package/dist/Sync.js CHANGED
@@ -1,10 +1,23 @@
1
- import { Effect, Either, Layer, ParseResult, pipe, Schema, Stream } from 'effect';
2
- import { Event as LedgerEvent, LedgerParameters } from '@midnight-ntwrk/ledger-v6';
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, Either, Layer, ParseResult, pipe, Schema, Stream, Duration, Chunk, Schedule } from 'effect';
14
+ import { Event as LedgerEvent, LedgerParameters } from '@midnight-ntwrk/ledger-v7';
3
15
  import { BlockHash, DustLedgerEvents } from '@midnight-ntwrk/wallet-sdk-indexer-client';
4
16
  import { WsSubscriptionClient, HttpQueryClient, ConnectionHelper, } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
5
17
  import { DateOps, EitherOps, LedgerOps } from '@midnight-ntwrk/wallet-sdk-utilities';
6
18
  import { WsURL } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
7
19
  import { WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
20
+ import { Simulator } from './Simulator.js';
8
21
  import { Uint8ArraySchema } from './Serialization.js';
9
22
  export const SecretKeysResource = {
10
23
  create: (secretKey) => {
@@ -45,10 +58,11 @@ export const SyncEventsUpdateSchema = Schema.Struct({
45
58
  maxId: Schema.Number,
46
59
  });
47
60
  export const WalletSyncUpdate = {
48
- create: (update, secretKey) => {
61
+ create: (updates, secretKey, timestamp) => {
49
62
  return {
50
- update,
63
+ updates,
51
64
  secretKeys: SecretKeysResource.create(secretKey),
65
+ timestamp,
52
66
  };
53
67
  },
54
68
  };
@@ -56,18 +70,26 @@ export const makeDefaultSyncService = (config) => {
56
70
  const indexerSyncService = makeIndexerSyncService(config);
57
71
  return {
58
72
  updates: (state, secretKey) => {
59
- return pipe(indexerSyncService.subscribeWallet(state), Stream.map((data) => WalletSyncUpdate.create(data, secretKey)), Stream.provideSomeLayer(indexerSyncService.connectionLayer()));
73
+ const batchSize = 10;
74
+ const batchTimeout = Duration.millis(1);
75
+ return pipe(indexerSyncService.subscribeWallet(state), Stream.groupedWithin(batchSize, batchTimeout), Stream.map(Chunk.toArray), Stream.map((data) => WalletSyncUpdate.create(data, secretKey, new Date())), Stream.schedule(Schedule.spaced(Duration.millis(4))), Stream.provideSomeLayer(indexerSyncService.connectionLayer()));
60
76
  },
61
- ledgerParameters: () => {
77
+ blockData: () => {
62
78
  return Effect.gen(function* () {
63
79
  const query = yield* BlockHash;
64
80
  const result = yield* query({ offset: null });
65
- return result.block?.ledgerParameters;
66
- }).pipe(Effect.provide(indexerSyncService.queryClient()), Effect.scoped, Effect.catchAll((err) => Effect.fail(WalletError.WalletError.other(`Encountered unexpected error: ${err.message}`))), Effect.flatMap((ledgerParameters) => {
67
- if (ledgerParameters === undefined) {
68
- return Effect.fail(WalletError.WalletError.other('Unable to fetch ledger parameters'));
81
+ return result.block;
82
+ }).pipe(Effect.provide(indexerSyncService.queryClient()), Effect.scoped, Effect.catchAll((err) => Effect.fail(WalletError.WalletError.other(`Encountered unexpected error: ${err.message}`))), Effect.flatMap((blockData) => {
83
+ if (!blockData) {
84
+ return Effect.fail(WalletError.WalletError.other('Unable to fetch block data'));
69
85
  }
70
- return LedgerOps.ledgerTry(() => LedgerParameters.deserialize(Buffer.from(ledgerParameters, 'hex')));
86
+ // TODO: convert to schema
87
+ return LedgerOps.ledgerTry(() => ({
88
+ hash: blockData.hash,
89
+ height: blockData.height,
90
+ ledgerParameters: LedgerParameters.deserialize(Buffer.from(blockData.ledgerParameters, 'hex')),
91
+ timestamp: new Date(blockData.timestamp),
92
+ }));
71
93
  }));
72
94
  },
73
95
  };
@@ -95,17 +117,22 @@ export const makeIndexerSyncService = (config) => {
95
117
  export const makeDefaultSyncCapability = () => {
96
118
  return {
97
119
  applyUpdate(state, wrappedUpdate) {
98
- const { update, secretKeys } = wrappedUpdate;
99
- const nextIndex = BigInt(update.id);
100
- const highestRelevantWalletIndex = BigInt(update.maxId);
120
+ const { updates, secretKeys } = wrappedUpdate;
121
+ // Nothing to update yet
122
+ if (updates.length === 0) {
123
+ return state;
124
+ }
125
+ const lastUpdate = updates.at(-1);
126
+ const nextIndex = BigInt(lastUpdate.id);
127
+ const highestRelevantWalletIndex = BigInt(lastUpdate.maxId);
101
128
  // in case the nextIndex is less than or equal to the current appliedIndex
102
129
  // just update highestRelevantWalletIndex
103
130
  if (nextIndex <= state.progress.appliedIndex) {
104
131
  return state.updateProgress({ highestRelevantWalletIndex, isConnected: true });
105
132
  }
106
- const events = [update.raw].filter((event) => event !== null);
133
+ const events = updates.map((u) => u.raw).filter((event) => event !== null);
107
134
  return secretKeys((keys) => state
108
- .applyEvents(keys, events, new Date())
135
+ .applyEvents(keys, events, wrappedUpdate.timestamp)
109
136
  .updateProgress({ appliedIndex: nextIndex, highestRelevantWalletIndex, isConnected: true }));
110
137
  },
111
138
  };
@@ -113,7 +140,18 @@ export const makeDefaultSyncCapability = () => {
113
140
  export const makeSimulatorSyncService = (config) => {
114
141
  return {
115
142
  updates: (_state, secretKey) => config.simulator.state$.pipe(Stream.map((state) => ({ update: state, secretKey }))),
116
- ledgerParameters: () => pipe(config.simulator.getLatestState(), Effect.map((state) => state.ledger.parameters)),
143
+ blockData: () => {
144
+ return Effect.gen(function* () {
145
+ const state = yield* config.simulator.getLatestState();
146
+ const timestamp = DateOps.secondsToDate(state.lastTxNumber);
147
+ return {
148
+ hash: yield* Simulator.blockHash(timestamp),
149
+ height: Number(state.lastTxNumber),
150
+ ledgerParameters: state.ledger.parameters,
151
+ timestamp,
152
+ };
153
+ });
154
+ },
117
155
  };
118
156
  };
119
157
  export const makeSimulatorSyncCapability = () => ({
@@ -1,22 +1,18 @@
1
1
  import { Either } from 'effect';
2
- import { DustSecretKey, Signature, SignatureVerifyingKey, Utxo, FinalizedTransaction, ProofErasedTransaction, UnprovenTransaction, LedgerParameters } from '@midnight-ntwrk/ledger-v6';
3
- import { ProvingRecipe, WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
2
+ import { DustSecretKey, Signature, SignatureVerifyingKey, FinalizedTransaction, ProofErasedTransaction, UnprovenTransaction, LedgerParameters } from '@midnight-ntwrk/ledger-v7';
3
+ import { WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
4
4
  import { DustCoreWallet } from './DustCoreWallet.js';
5
5
  import { AnyTransaction, DustToken, NetworkId, TotalCostParameters } from './types/index.js';
6
- import { CoinsAndBalancesCapability, CoinSelection, CoinWithValue } from './CoinsAndBalances.js';
6
+ import { CoinsAndBalancesCapability, CoinSelection, UtxoWithFullDustDetails } from './CoinsAndBalances.js';
7
7
  import { KeysCapability } from './Keys.js';
8
8
  export interface TransactingCapability<TSecrets, TState, TTransaction> {
9
9
  readonly networkId: NetworkId;
10
10
  readonly costParams: TotalCostParameters;
11
- createDustGenerationTransaction(currentTime: Date, ttl: Date, nightUtxos: ReadonlyArray<CoinWithValue<Utxo>>, nightVerifyingKey: SignatureVerifyingKey, dustReceiverAddress: string | undefined): Either.Either<UnprovenTransaction, WalletError.WalletError>;
12
- addDustGenerationSignature(transaction: UnprovenTransaction, signature: Signature): Either.Either<ProvingRecipe.ProvingRecipe<FinalizedTransaction>, WalletError.WalletError>;
11
+ createDustGenerationTransaction(currentTime: Date, ttl: Date, nightUtxos: ReadonlyArray<UtxoWithFullDustDetails>, nightVerifyingKey: SignatureVerifyingKey, dustReceiverAddress: string | undefined): Either.Either<UnprovenTransaction, WalletError.WalletError>;
12
+ addDustGenerationSignature(transaction: UnprovenTransaction, signature: Signature): Either.Either<UnprovenTransaction, WalletError.WalletError>;
13
13
  calculateFee(transaction: AnyTransaction, ledgerParams: LedgerParameters): bigint;
14
- addFeePayment(secretKey: TSecrets, state: TState, transaction: UnprovenTransaction, currentTime: Date, ttl: Date, ledgerParams: LedgerParameters): Either.Either<{
15
- recipe: ProvingRecipe.ProvingRecipe<FinalizedTransaction>;
16
- newState: TState;
17
- }, WalletError.WalletError>;
18
- revert(state: TState, tx: TTransaction): Either.Either<TState, WalletError.WalletError>;
19
- revertRecipe(state: TState, recipe: ProvingRecipe.ProvingRecipe<TTransaction>): Either.Either<TState, WalletError.WalletError>;
14
+ balanceTransactions(secretKey: TSecrets, state: TState, transactions: ReadonlyArray<AnyTransaction>, ttl: Date, currentTime: Date, ledgerParams: LedgerParameters): Either.Either<[UnprovenTransaction, TState], WalletError.WalletError>;
15
+ revertTransaction(state: TState, transaction: UnprovenTransaction | TTransaction): Either.Either<TState, WalletError.WalletError>;
20
16
  }
21
17
  export type DefaultTransactingConfiguration = {
22
18
  networkId: NetworkId;
@@ -37,14 +33,10 @@ export declare class TransactingCapabilityImplementation<TTransaction extends An
37
33
  readonly getCoins: () => CoinsAndBalancesCapability<DustCoreWallet>;
38
34
  readonly getKeys: () => KeysCapability<DustCoreWallet>;
39
35
  constructor(networkId: NetworkId, costParams: TotalCostParameters, getCoinSelection: () => CoinSelection<DustToken>, getCoins: () => CoinsAndBalancesCapability<DustCoreWallet>, getKeys: () => KeysCapability<DustCoreWallet>);
40
- createDustGenerationTransaction(currentTime: Date, ttl: Date, nightUtxos: ReadonlyArray<CoinWithValue<Utxo>>, nightVerifyingKey: SignatureVerifyingKey, dustReceiverAddress: string | undefined): Either.Either<UnprovenTransaction, WalletError.WalletError>;
41
- addDustGenerationSignature(transaction: UnprovenTransaction, signatureData: Signature): Either.Either<ProvingRecipe.ProvingRecipe<FinalizedTransaction>, WalletError.WalletError>;
36
+ createDustGenerationTransaction(currentTime: Date, ttl: Date, nightUtxos: ReadonlyArray<UtxoWithFullDustDetails>, nightVerifyingKey: SignatureVerifyingKey, dustReceiverAddress: string | undefined): Either.Either<UnprovenTransaction, WalletError.WalletError>;
37
+ addDustGenerationSignature(transaction: UnprovenTransaction, signatureData: Signature): Either.Either<UnprovenTransaction, WalletError.WalletError>;
42
38
  calculateFee(transaction: AnyTransaction, ledgerParams: LedgerParameters): bigint;
43
39
  static feeImbalance(transaction: AnyTransaction, totalFee: bigint): bigint;
44
- addFeePayment(secretKey: DustSecretKey, state: DustCoreWallet, transaction: UnprovenTransaction, currentTime: Date, ttl: Date, ledgerParams: LedgerParameters): Either.Either<{
45
- recipe: ProvingRecipe.ProvingRecipe<FinalizedTransaction>;
46
- newState: DustCoreWallet;
47
- }, WalletError.WalletError>;
48
- revert(state: DustCoreWallet, tx: TTransaction): Either.Either<DustCoreWallet, WalletError.WalletError>;
49
- revertRecipe(state: DustCoreWallet, recipe: ProvingRecipe.ProvingRecipe<TTransaction>): Either.Either<DustCoreWallet, WalletError.WalletError>;
40
+ balanceTransactions(secretKey: DustSecretKey, state: DustCoreWallet, transactions: ReadonlyArray<FinalizedTransaction | UnprovenTransaction>, ttl: Date, currentTime: Date, ledgerParams: LedgerParameters): Either.Either<[UnprovenTransaction, DustCoreWallet], WalletError.WalletError>;
41
+ revertTransaction(state: DustCoreWallet, transaction: UnprovenTransaction | TTransaction): Either.Either<DustCoreWallet, WalletError.WalletError>;
50
42
  }
@@ -1,7 +1,19 @@
1
- import { Either } from 'effect';
2
- import { DustActions, DustRegistration, Intent, SignatureEnabled, Transaction, UnshieldedOffer, addressFromKey, } from '@midnight-ntwrk/ledger-v6';
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 { Either, pipe, BigInt as BigIntOps, Iterable as IterableOps, Option } from 'effect';
14
+ import { DustActions, DustRegistration, Intent, SignatureEnabled, Transaction, UnshieldedOffer, addressFromKey, nativeToken, } from '@midnight-ntwrk/ledger-v7';
3
15
  import { MidnightBech32m, DustAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
4
- import { ProvingRecipe, WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
16
+ import { WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
5
17
  import { LedgerOps } from '@midnight-ntwrk/wallet-sdk-utilities';
6
18
  import { BindingMarker, ProofMarker, SignatureMarker } from './Utils.js';
7
19
  export const makeDefaultTransactingCapability = (config, getContext) => {
@@ -24,24 +36,48 @@ export class TransactingCapabilityImplementation {
24
36
  this.getKeys = getKeys;
25
37
  }
26
38
  createDustGenerationTransaction(currentTime, ttl, nightUtxos, nightVerifyingKey, dustReceiverAddress) {
39
+ const makeOffer = (utxos) => {
40
+ if (utxos.length === 0) {
41
+ return Option.none();
42
+ }
43
+ const totalValue = pipe(utxos, IterableOps.map((coin) => coin.utxo.value), BigIntOps.sumAll);
44
+ const inputs = utxos.map(({ utxo }) => ({
45
+ ...utxo,
46
+ owner: nightVerifyingKey,
47
+ }));
48
+ const output = {
49
+ owner: addressFromKey(nightVerifyingKey),
50
+ type: nativeToken().raw,
51
+ value: totalValue,
52
+ };
53
+ return Option.some(UnshieldedOffer.new(inputs, [output], []));
54
+ };
27
55
  return Either.gen(this, function* () {
28
56
  const receiver = dustReceiverAddress ? yield* this.#parseAddress(dustReceiverAddress) : undefined;
29
57
  return yield* LedgerOps.ledgerTry(() => {
30
58
  const network = this.networkId;
31
- const intent = Intent.new(ttl);
32
- const totalDustValue = nightUtxos.reduce((total, { value }) => total + value, 0n);
33
- const inputs = nightUtxos.map(({ token: utxo }) => ({
34
- ...utxo,
35
- owner: nightVerifyingKey,
36
- }));
37
- const outputs = inputs.map((input) => ({
38
- owner: addressFromKey(nightVerifyingKey),
39
- type: input.type,
40
- value: input.value,
41
- }));
42
- intent.guaranteedUnshieldedOffer = UnshieldedOffer.new(inputs, outputs, []);
59
+ const splitResult = this.getCoins().splitNightUtxos(nightUtxos);
60
+ const totalDustValue = pipe(splitResult.guaranteed, IterableOps.map((coin) => coin.dust.generatedNow), BigIntOps.sumAll);
61
+ const maybeGuaranteedOffer = makeOffer(splitResult.guaranteed);
62
+ const maybeFallibleOffer = makeOffer(splitResult.fallible);
43
63
  const dustRegistration = new DustRegistration(SignatureMarker.signature, nightVerifyingKey, receiver, dustReceiverAddress !== undefined ? totalDustValue : 0n);
44
- intent.dustActions = new DustActions(SignatureMarker.signature, ProofMarker.preProof, currentTime, [], [dustRegistration]);
64
+ const dustActions = new DustActions(SignatureMarker.signature, ProofMarker.preProof, currentTime, [], [dustRegistration]);
65
+ const intent = pipe(Intent.new(ttl), (intent) => Option.match(maybeGuaranteedOffer, {
66
+ onNone: () => intent,
67
+ onSome: (guaranteedOffer) => {
68
+ intent.guaranteedUnshieldedOffer = guaranteedOffer;
69
+ return intent;
70
+ },
71
+ }), (intent) => Option.match(maybeFallibleOffer, {
72
+ onNone: () => intent,
73
+ onSome: (fallibleOffer) => {
74
+ intent.fallibleUnshieldedOffer = fallibleOffer;
75
+ return intent;
76
+ },
77
+ }), (intent) => {
78
+ intent.dustActions = dustActions;
79
+ return intent;
80
+ });
45
81
  return Transaction.fromParts(network, undefined, undefined, intent);
46
82
  });
47
83
  });
@@ -52,7 +88,7 @@ export class TransactingCapabilityImplementation {
52
88
  if (!intent) {
53
89
  return yield* Either.left(new WalletError.TransactingError({ message: 'No intent found in the transaction intents with segment = 1' }));
54
90
  }
55
- const { dustActions, guaranteedUnshieldedOffer } = intent;
91
+ const { dustActions, guaranteedUnshieldedOffer, fallibleUnshieldedOffer } = intent;
56
92
  if (!dustActions) {
57
93
  return yield* Either.left(new WalletError.TransactingError({ message: 'No dustActions found in intent' }));
58
94
  }
@@ -76,13 +112,18 @@ export class TransactingCapabilityImplementation {
76
112
  signatures.push(guaranteedUnshieldedOffer.signatures.at(i) ?? signatureData);
77
113
  }
78
114
  newIntent.guaranteedUnshieldedOffer = guaranteedUnshieldedOffer.addSignatures(signatures);
115
+ if (fallibleUnshieldedOffer) {
116
+ const inputsLen = fallibleUnshieldedOffer.inputs.length;
117
+ const signatures = [];
118
+ for (let i = 0; i < inputsLen; ++i) {
119
+ signatures.push(fallibleUnshieldedOffer.signatures.at(i) ?? signatureData);
120
+ }
121
+ newIntent.fallibleUnshieldedOffer = fallibleUnshieldedOffer.addSignatures(signatures);
122
+ }
79
123
  // make a copy of transaction to avoid mutation
80
124
  const newTransaction = Transaction.deserialize(signature.instance, ProofMarker.preProof, BindingMarker.preBinding, transaction.serialize());
81
125
  newTransaction.intents = newTransaction.intents.set(1, newIntent);
82
- return {
83
- type: ProvingRecipe.TRANSACTION_TO_PROVE,
84
- transaction: newTransaction,
85
- };
126
+ return newTransaction;
86
127
  });
87
128
  });
88
129
  }
@@ -96,9 +137,10 @@ export class TransactingCapabilityImplementation {
96
137
  .find(([tt, _]) => tt.tag === 'dust');
97
138
  return dustImbalance ? -dustImbalance[1] : totalFee;
98
139
  }
99
- addFeePayment(secretKey, state, transaction, currentTime, ttl, ledgerParams) {
140
+ balanceTransactions(secretKey, state, transactions, ttl, currentTime, ledgerParams) {
100
141
  const network = this.networkId;
101
- const feeLeft = TransactingCapabilityImplementation.feeImbalance(transaction, this.calculateFee(transaction, ledgerParams));
142
+ const feeLeft = transactions.reduce((total, transaction) => total +
143
+ TransactingCapabilityImplementation.feeImbalance(transaction, this.calculateFee(transaction, ledgerParams)), 0n);
102
144
  const dustTokens = this.getCoins().getAvailableCoinsWithGeneratedDust(state, currentTime);
103
145
  const selectedTokens = this.getCoinSelection()(dustTokens, feeLeft);
104
146
  if (!selectedTokens.length) {
@@ -107,8 +149,7 @@ export class TransactingCapabilityImplementation {
107
149
  const totalFeeInSelected = selectedTokens.reduce((total, { value }) => total + value, 0n);
108
150
  const feeDiff = totalFeeInSelected - feeLeft;
109
151
  if (feeDiff < 0n) {
110
- // A sanity-check, should never happen
111
- return Either.left(new WalletError.TransactingError({ message: 'Error in tokens selection algorithm' }));
152
+ return Either.left(new WalletError.TransactingError({ message: 'Not enough Dust generated to pay the fee' }));
112
153
  }
113
154
  // reduce the largest token's value by `feeDiff`
114
155
  const tokensWithFeeToTake = selectedTokens.toSorted((a, b) => Number(b.value - a.value));
@@ -122,49 +163,22 @@ export class TransactingCapabilityImplementation {
122
163
  return LedgerOps.ledgerTry(() => {
123
164
  const intent = Intent.new(ttl);
124
165
  const [spends, updatedState] = state.spendCoins(secretKey, tokensWithFeeToTake, currentTime);
125
- intent.dustActions = new DustActions(SignatureMarker.signature, ProofMarker.preProof, currentTime, spends, []);
166
+ intent.dustActions = new DustActions(SignatureMarker.signature, ProofMarker.preProof, currentTime, [...spends], []);
126
167
  const feeTransaction = Transaction.fromPartsRandomized(network, undefined, undefined, intent);
127
- return {
128
- newState: updatedState,
129
- recipe: {
130
- type: ProvingRecipe.TRANSACTION_TO_PROVE,
131
- transaction: transaction.merge(feeTransaction),
132
- },
133
- };
168
+ return [feeTransaction, updatedState];
134
169
  });
135
170
  }
136
- revert(state, tx) {
171
+ revertTransaction(state, transaction) {
137
172
  return Either.try({
138
- try: () => state.revertTransaction(tx),
173
+ try: () => state.revertTransaction(transaction),
139
174
  catch: (err) => {
140
175
  return new WalletError.OtherWalletError({
141
- message: `Error while reverting transaction ${tx.identifiers().at(0)}`,
176
+ message: `Error while reverting transaction ${transaction.identifiers().at(0)}`,
142
177
  cause: err,
143
178
  });
144
179
  },
145
180
  });
146
181
  }
147
- revertRecipe(state, recipe) {
148
- const doRevert = (tx) => {
149
- return Either.try({
150
- try: () => state.revertTransaction(tx),
151
- catch: (err) => {
152
- return new WalletError.OtherWalletError({
153
- message: `Error while reverting transaction ${tx.identifiers().at(0)}`,
154
- cause: err,
155
- });
156
- },
157
- });
158
- };
159
- switch (recipe.type) {
160
- case ProvingRecipe.TRANSACTION_TO_PROVE:
161
- return doRevert(recipe.transaction);
162
- case ProvingRecipe.BALANCE_TRANSACTION_TO_PROVE:
163
- return doRevert(recipe.transactionToProve);
164
- case ProvingRecipe.NOTHING_TO_PROVE:
165
- return Either.right(state);
166
- }
167
- }
168
182
  #parseAddress(addr) {
169
183
  return Either.try({
170
184
  try: () => {
package/dist/Utils.js CHANGED
@@ -1,3 +1,15 @@
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.
1
13
  export const SignatureMarker = {
2
14
  signature: 'signature',
3
15
  signatureErased: 'signature-erased',
@@ -1,12 +1,12 @@
1
1
  import { Types, Either } from 'effect';
2
- import { DustSecretKey, FinalizedTransaction } from '@midnight-ntwrk/ledger-v6';
2
+ import { DustSecretKey, FinalizedTransaction } from '@midnight-ntwrk/ledger-v7';
3
3
  import { VariantBuilder, Variant } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
4
4
  import { Proving, WalletError } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
5
5
  import { SyncService, SyncCapability, DefaultSyncConfiguration, WalletSyncUpdate } from './Sync.js';
6
6
  import { RunningV1Variant, V1Tag } from './RunningV1Variant.js';
7
7
  import { DustCoreWallet } from './DustCoreWallet.js';
8
8
  import { KeysCapability } from './Keys.js';
9
- import { CoinsAndBalancesCapability, CoinSelection } from './CoinsAndBalances.js';
9
+ import { CoinsAndBalancesCapability, CoinSelection, DefaultCoinsAndBalancesContext } from './CoinsAndBalances.js';
10
10
  import { DefaultTransactingConfiguration, DefaultTransactingContext, TransactingCapability } from './Transacting.js';
11
11
  import { NetworkId } from './types/ledger.js';
12
12
  import { DefaultSubmissionConfiguration, SubmissionService } from './Submission.js';
@@ -46,7 +46,7 @@ export declare class V1Builder<TConfig extends BaseV1Configuration = BaseV1Confi
46
46
  withCoinSelectionDefaults(): V1Builder<TConfig, TContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
47
47
  withProving<TProvingConfig, TProvingContext extends Partial<RunningV1Variant.AnyContext>>(provingService: (config: TProvingConfig, getContext: () => TProvingContext) => Proving.ProvingService<TTransaction>): V1Builder<TConfig & TProvingConfig, TContext & TProvingContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
48
48
  withProvingDefaults(this: V1Builder<TConfig, TContext, TSerialized, TSyncUpdate, FinalizedTransaction, TStartAux>): V1Builder<TConfig & Proving.DefaultProvingConfiguration, TContext, TSerialized, TSyncUpdate, FinalizedTransaction, TStartAux>;
49
- withCoinsAndBalancesDefaults(): V1Builder<TConfig, TContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
49
+ withCoinsAndBalancesDefaults(): V1Builder<TConfig, TContext & DefaultCoinsAndBalancesContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
50
50
  withCoinsAndBalances<TBalancesConfig, TBalancesContext extends Partial<RunningV1Variant.AnyContext>>(coinsAndBalancesCapability: (configuration: TBalancesConfig, getContext: () => TBalancesContext) => CoinsAndBalancesCapability<DustCoreWallet>): V1Builder<TConfig & TBalancesConfig, TContext & TBalancesContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
51
51
  withKeysDefaults(): V1Builder<TConfig, TContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
52
52
  withKeys<TKeysConfig, TKeysContext extends Partial<RunningV1Variant.AnyContext>>(keysCapability: (configuration: TKeysConfig, getContext: () => TKeysContext) => KeysCapability<DustCoreWallet>): V1Builder<TConfig & TKeysConfig, TContext & TKeysContext, TSerialized, TSyncUpdate, TTransaction, TStartAux>;
package/dist/V1Builder.js CHANGED
@@ -1,3 +1,15 @@
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.
1
13
  import { Effect, Scope } from 'effect';
2
14
  import { Proving } from '@midnight-ntwrk/wallet-sdk-shielded/v1';
3
15
  import { makeDefaultSyncCapability, makeDefaultSyncService, } from './Sync.js';
package/dist/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * as Transacting from './Transacting.js';
7
7
  export * from './RunningV1Variant.js';
8
8
  export * from './V1Builder.js';
9
9
  export * from './types/index.js';
10
+ export * as CoinsAndBalances from './CoinsAndBalances.js';
package/dist/index.js CHANGED
@@ -1,3 +1,15 @@
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.
1
13
  export * from './DustCoreWallet.js';
2
14
  export * from './DustWallet.js';
3
15
  export * as Keys from './Keys.js';
@@ -7,3 +19,4 @@ export * as Transacting from './Transacting.js';
7
19
  export * from './RunningV1Variant.js';
8
20
  export * from './V1Builder.js';
9
21
  export * from './types/index.js';
22
+ export * as CoinsAndBalances from './CoinsAndBalances.js';
@@ -1,4 +1,4 @@
1
- import { DustInitialNonce, DustNullifier, DustNonce, DustPublicKey, Utxo } from '@midnight-ntwrk/ledger-v6';
1
+ import { DustInitialNonce, DustNullifier, DustNonce, DustPublicKey, Utxo } from '@midnight-ntwrk/ledger-v7';
2
2
  export type DustToken = {
3
3
  initialValue: bigint;
4
4
  owner: DustPublicKey;
@@ -11,8 +11,10 @@ export type DustToken = {
11
11
  export type DustTokenWithNullifier = DustToken & {
12
12
  nullifier: DustNullifier;
13
13
  };
14
- export type DustTokenFullInfo = {
14
+ export type DustTokenFullInfo = DustGenerationDetails & {
15
15
  token: DustToken;
16
+ };
17
+ export type DustGenerationDetails = {
16
18
  dtime: Date | undefined;
17
19
  maxCap: bigint;
18
20
  maxCapReachedAt: Date;
@@ -1,3 +1,15 @@
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.
1
13
  export * from './Dust.js';
2
14
  export * from './ledger.js';
3
15
  export * from './transaction.js';
@@ -1,4 +1,4 @@
1
- import * as ledger from '@midnight-ntwrk/ledger-v6';
2
- export type AnyTransaction = ledger.UnprovenTransaction | ledger.FinalizedTransaction | ledger.ProofErasedTransaction;
1
+ import * as ledger from '@midnight-ntwrk/ledger-v7';
2
+ export type AnyTransaction = ledger.UnprovenTransaction | ledger.FinalizedTransaction | ledger.ProofErasedTransaction | ledger.Transaction<ledger.SignatureEnabled, ledger.Proof, ledger.PreBinding>;
3
3
  export type UnprovenDustSpend = ledger.DustSpend<ledger.PreProof>;
4
4
  export type NetworkId = string;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@midnight-ntwrk/wallet-sdk-dust-wallet",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
- "author": "IOHK",
7
+ "author": "Midnight Foundation",
8
8
  "license": "Apache-2.0",
9
9
  "publishConfig": {
10
10
  "registry": "https://npm.pkg.github.com/"
@@ -23,24 +23,25 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@midnight-ntwrk/ledger-v6": "6.1.0-alpha.5",
27
- "@midnight-ntwrk/wallet-sdk-abstractions": "1.0.0-beta.8",
28
- "@midnight-ntwrk/wallet-sdk-address-format": "3.0.0-beta.7",
29
- "@midnight-ntwrk/wallet-sdk-capabilities": "3.0.0-beta.7",
30
- "@midnight-ntwrk/wallet-sdk-hd": "3.0.0-beta.6",
31
- "@midnight-ntwrk/wallet-sdk-indexer-client": "1.0.0-beta.11",
32
- "@midnight-ntwrk/wallet-sdk-node-client": "1.0.0-beta.9",
33
- "@midnight-ntwrk/wallet-sdk-prover-client": "1.0.0-beta.8",
34
- "@midnight-ntwrk/wallet-sdk-shielded": "1.0.0-beta.9",
35
- "@midnight-ntwrk/wallet-sdk-utilities": "1.0.0-beta.7",
36
- "effect": "^3.17.3",
26
+ "@midnight-ntwrk/ledger-v7": "7.0.0",
27
+ "@midnight-ntwrk/wallet-sdk-abstractions": "1.0.0",
28
+ "@midnight-ntwrk/wallet-sdk-address-format": "3.0.0",
29
+ "@midnight-ntwrk/wallet-sdk-capabilities": "3.0.0",
30
+ "@midnight-ntwrk/wallet-sdk-hd": "3.0.0",
31
+ "@midnight-ntwrk/wallet-sdk-indexer-client": "1.0.0",
32
+ "@midnight-ntwrk/wallet-sdk-node-client": "1.0.0",
33
+ "@midnight-ntwrk/wallet-sdk-prover-client": "1.0.0",
34
+ "@midnight-ntwrk/wallet-sdk-shielded": "1.0.0",
35
+ "@midnight-ntwrk/wallet-sdk-utilities": "1.0.0",
36
+ "effect": "^3.19.14",
37
37
  "rxjs": "^7.5"
38
38
  },
39
39
  "scripts": {
40
40
  "typecheck": "tsc -b ./tsconfig.json --noEmit",
41
41
  "test": "vitest run",
42
42
  "lint": "eslint --max-warnings 0",
43
- "format": "prettier --write \"**/*.{ts,js,json,yaml,yml}\"",
43
+ "format": "prettier --write \"**/*.{ts,js,json,yaml,yml,md}\"",
44
+ "format:check": "prettier --check \"**/*.{ts,js,json,yaml,yml,md}\"",
44
45
  "dist": "tsc -b ./tsconfig.build.json",
45
46
  "dist:publish": "tsc -b ./tsconfig.publish.json",
46
47
  "clean": "rimraf --glob dist 'tsconfig.*.tsbuildinfo' && date +%s > .clean-timestamp",
@@ -49,10 +50,11 @@
49
50
  "devDependencies": {
50
51
  "@types/node": "22.17.0",
51
52
  "eslint": "^9.37.0",
53
+ "prettier": "^3.7.0",
52
54
  "publint": "~0.3.14",
53
55
  "rimraf": "^6.0.1",
54
- "testcontainers": "^11.0.3",
56
+ "testcontainers": "^11.10.0",
55
57
  "typescript": "^5.9.3",
56
- "vitest": "^3.2.4"
58
+ "vitest": "^4.0.16"
57
59
  }
58
60
  }