@midnight-ntwrk/wallet-sdk-shielded 2.1.0-rc.0 → 3.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.
- package/dist/ShieldedWallet.d.ts +11 -13
- package/dist/ShieldedWallet.js +9 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/v1/CoinsAndBalances.d.ts +2 -2
- package/dist/v1/CoreWallet.d.ts +2 -2
- package/dist/v1/CoreWallet.js +5 -3
- package/dist/v1/RunningV1Variant.d.ts +4 -4
- package/dist/v1/RunningV1Variant.js +10 -9
- package/dist/v1/Serialization.d.ts +1 -1
- package/dist/v1/Sync.d.ts +39 -13
- package/dist/v1/Sync.js +65 -27
- package/dist/v1/Transacting.d.ts +7 -7
- package/dist/v1/Transacting.js +25 -21
- package/dist/v1/TransactionHistory.d.ts +63 -8
- package/dist/v1/TransactionHistory.js +90 -23
- package/dist/v1/TransactionImbalances.d.ts +2 -2
- package/dist/v1/TransactionImbalances.js +3 -3
- package/dist/v1/TransactionOps.d.ts +3 -3
- package/dist/v1/TransactionOps.js +11 -14
- package/dist/v1/V1Builder.d.ts +13 -17
- package/dist/v1/V1Builder.js +9 -9
- package/dist/v1/WalletError.d.ts +17 -9
- package/dist/v1/WalletError.js +2 -0
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/index.js +1 -1
- package/package.json +8 -8
- package/dist/v1/Simulator.d.ts +0 -22
- package/dist/v1/Simulator.js +0 -103
package/dist/ShieldedWallet.d.ts
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
import { type ProtocolState, ProtocolVersion, SyncProgress } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
1
|
+
import { type ProtocolState, ProtocolVersion, type SyncProgress } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
2
2
|
import { type BaseV1Configuration, type DefaultV1Configuration, type V1Variant, CoreWallet } from './v1/index.js';
|
|
3
3
|
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
4
4
|
import * as rx from 'rxjs';
|
|
5
5
|
import { type BalancingResult } from './v1/Transacting.js';
|
|
6
6
|
import { type SerializationCapability } from './v1/Serialization.js';
|
|
7
|
-
import { type
|
|
7
|
+
import { type TransactionHistoryService } from './v1/TransactionHistory.js';
|
|
8
8
|
import { type AvailableCoin, type CoinsAndBalancesCapability, type PendingCoin } from './v1/CoinsAndBalances.js';
|
|
9
9
|
import { type KeysCapability } from './v1/Keys.js';
|
|
10
10
|
import { type ShieldedAddress, type ShieldedCoinPublicKey, type ShieldedEncryptionPublicKey } from '@midnight-ntwrk/wallet-sdk-address-format';
|
|
11
11
|
import { type TokenTransfer } from './v1/Transacting.js';
|
|
12
12
|
import { type WalletSyncUpdate } from './v1/Sync.js';
|
|
13
13
|
import { type Variant, type VariantBuilder, type WalletLike } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
|
|
14
|
-
export type ShieldedWalletCapabilities<TSerialized = string
|
|
14
|
+
export type ShieldedWalletCapabilities<TSerialized = string> = {
|
|
15
15
|
serialization: SerializationCapability<CoreWallet, null, TSerialized>;
|
|
16
16
|
coinsAndBalances: CoinsAndBalancesCapability<CoreWallet>;
|
|
17
17
|
keys: KeysCapability<CoreWallet>;
|
|
18
|
-
|
|
18
|
+
};
|
|
19
|
+
export type ShieldedWalletServices = {
|
|
20
|
+
transactionHistory: TransactionHistoryService;
|
|
19
21
|
};
|
|
20
22
|
export type UnboundTransaction = ledger.Transaction<ledger.SignatureEnabled, ledger.Proof, ledger.PreBinding>;
|
|
21
|
-
export declare class ShieldedWalletState<TSerialized = string,
|
|
22
|
-
static readonly mapState: <TSerialized_1 = string
|
|
23
|
+
export declare class ShieldedWalletState<TSerialized = string, _TTransaction = ledger.FinalizedTransaction> {
|
|
24
|
+
static readonly mapState: <TSerialized_1 = string>(variant: ShieldedWalletCapabilities<TSerialized_1> & ShieldedWalletServices) => (state: ProtocolState.ProtocolState<CoreWallet>) => ShieldedWalletState<TSerialized_1>;
|
|
23
25
|
readonly protocolVersion: ProtocolVersion.ProtocolVersion;
|
|
24
26
|
readonly state: CoreWallet;
|
|
25
|
-
readonly capabilities: ShieldedWalletCapabilities<TSerialized
|
|
27
|
+
readonly capabilities: ShieldedWalletCapabilities<TSerialized>;
|
|
28
|
+
readonly services: ShieldedWalletServices;
|
|
26
29
|
get balances(): Record<ledger.RawTokenType, bigint>;
|
|
27
30
|
get totalCoins(): readonly (AvailableCoin | PendingCoin)[];
|
|
28
31
|
get availableCoins(): readonly AvailableCoin[];
|
|
@@ -31,12 +34,7 @@ export declare class ShieldedWalletState<TSerialized = string, TTransaction = le
|
|
|
31
34
|
get encryptionPublicKey(): ShieldedEncryptionPublicKey;
|
|
32
35
|
get address(): ShieldedAddress;
|
|
33
36
|
get progress(): SyncProgress.SyncProgress;
|
|
34
|
-
|
|
35
|
-
* Transaction history for the wallet.
|
|
36
|
-
* @throws Error - Not yet implemented
|
|
37
|
-
*/
|
|
38
|
-
get transactionHistory(): never;
|
|
39
|
-
constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: ShieldedWalletCapabilities<TSerialized, TTransaction>);
|
|
37
|
+
constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: ShieldedWalletCapabilities<TSerialized>, services: ShieldedWalletServices);
|
|
40
38
|
serialize(): TSerialized;
|
|
41
39
|
}
|
|
42
40
|
export type ShieldedWallet = CustomizedShieldedWallet<ledger.ZswapSecretKeys, ledger.FinalizedTransaction, WalletSyncUpdate, string>;
|
package/dist/ShieldedWallet.js
CHANGED
|
@@ -17,12 +17,15 @@ import { Effect, Either } from 'effect';
|
|
|
17
17
|
import * as rx from 'rxjs';
|
|
18
18
|
import { WalletBuilder } from '@midnight-ntwrk/wallet-sdk-runtime';
|
|
19
19
|
export class ShieldedWalletState {
|
|
20
|
-
static mapState = (
|
|
21
|
-
|
|
20
|
+
static mapState = (variant) => (state) => {
|
|
21
|
+
const { serialization, coinsAndBalances, keys } = variant;
|
|
22
|
+
const { transactionHistory } = variant;
|
|
23
|
+
return new ShieldedWalletState(state, { serialization, coinsAndBalances, keys }, { transactionHistory });
|
|
22
24
|
};
|
|
23
25
|
protocolVersion;
|
|
24
26
|
state;
|
|
25
27
|
capabilities;
|
|
28
|
+
services;
|
|
26
29
|
get balances() {
|
|
27
30
|
return this.capabilities.coinsAndBalances.getAvailableBalances(this.state);
|
|
28
31
|
}
|
|
@@ -47,17 +50,11 @@ export class ShieldedWalletState {
|
|
|
47
50
|
get progress() {
|
|
48
51
|
return this.state.progress;
|
|
49
52
|
}
|
|
50
|
-
|
|
51
|
-
* Transaction history for the wallet.
|
|
52
|
-
* @throws Error - Not yet implemented
|
|
53
|
-
*/
|
|
54
|
-
get transactionHistory() {
|
|
55
|
-
throw new Error('Transaction history is not yet implemented for ShieldedWallet');
|
|
56
|
-
}
|
|
57
|
-
constructor(state, capabilities) {
|
|
53
|
+
constructor(state, capabilities, services) {
|
|
58
54
|
this.protocolVersion = state.version;
|
|
59
55
|
this.state = state.state;
|
|
60
56
|
this.capabilities = capabilities;
|
|
57
|
+
this.services = services;
|
|
61
58
|
}
|
|
62
59
|
serialize() {
|
|
63
60
|
return this.capabilities.serialization.serialize(this.state);
|
|
@@ -122,8 +119,8 @@ export function CustomShieldedWallet(configuration, builder) {
|
|
|
122
119
|
return rx.firstValueFrom(this.state.pipe(rx.filter((state) => state.state.progress.isCompleteWithin(allowedGap))));
|
|
123
120
|
}
|
|
124
121
|
/**
|
|
125
|
-
* Serializes the most recent state
|
|
126
|
-
*
|
|
122
|
+
* Serializes the most recent state It's preferable to use [[ShieldedWalletState.serialize]] instead, to know
|
|
123
|
+
* exactly, which state is serialized
|
|
127
124
|
*/
|
|
128
125
|
serializeState() {
|
|
129
126
|
return rx.firstValueFrom(this.state).then((state) => state.serialize());
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CoreWallet } from './CoreWallet.js';
|
|
2
|
-
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
1
|
+
import { type CoreWallet } from './CoreWallet.js';
|
|
2
|
+
import type * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
3
3
|
export type AvailableCoin = {
|
|
4
4
|
coin: ledger.QualifiedShieldedCoinInfo;
|
|
5
5
|
commitment: ledger.CoinCommitment;
|
package/dist/v1/CoreWallet.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
2
2
|
import { ProtocolVersion, SyncProgress } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
3
3
|
import { Either } from 'effect';
|
|
4
|
-
import { WalletError } from './WalletError.js';
|
|
4
|
+
import { type WalletError } from './WalletError.js';
|
|
5
5
|
export type PublicKeys = {
|
|
6
6
|
coinPublicKey: ledger.CoinPublicKey;
|
|
7
7
|
encryptionPublicKey: ledger.EncPublicKey;
|
|
@@ -37,7 +37,7 @@ export declare const CoreWallet: {
|
|
|
37
37
|
initEmpty(keys: ledger.ZswapSecretKeys, networkId: string): CoreWallet;
|
|
38
38
|
applyCollapsedUpdate(wallet: CoreWallet, collapsed: ledger.MerkleTreeCollapsedUpdate): CoreWallet;
|
|
39
39
|
apply<TOffer extends ledger.ZswapOffer<ledger.Proofish>>(wallet: CoreWallet, secretKeys: ledger.ZswapSecretKeys, offer: TOffer): CoreWallet;
|
|
40
|
-
|
|
40
|
+
replayEventsWithChanges(wallet: CoreWallet, secretKeys: ledger.ZswapSecretKeys, events: ledger.Event[]): [CoreWallet, ledger.ZswapStateChanges[]];
|
|
41
41
|
updateProgress(wallet: CoreWallet, { appliedIndex, highestRelevantWalletIndex, highestIndex, highestRelevantIndex, isConnected, }: Partial<SyncProgress.SyncProgressData>): CoreWallet;
|
|
42
42
|
addTransaction(wallet: CoreWallet, _tx: ledger.FinalizedTransaction): CoreWallet;
|
|
43
43
|
revertTransaction<TTx extends ledger.Transaction<ledger.Signaturish, ledger.Proofish, ledger.Bindingish>>(wallet: CoreWallet, tx: TTx): CoreWallet;
|
package/dist/v1/CoreWallet.js
CHANGED
|
@@ -106,10 +106,12 @@ export const CoreWallet = {
|
|
|
106
106
|
const newCoinHashes = CoinHashesMap.updateWithCoins(secretKeys, wallet.coinHashes, CoinHashesMap.pickAllCoins(newState));
|
|
107
107
|
return { ...wallet, state: newState, coinHashes: newCoinHashes };
|
|
108
108
|
},
|
|
109
|
-
|
|
110
|
-
const
|
|
109
|
+
replayEventsWithChanges(wallet, secretKeys, events) {
|
|
110
|
+
const stateWithChanges = wallet.state.replayEventsWithChanges(secretKeys, events);
|
|
111
|
+
const newState = stateWithChanges.state;
|
|
111
112
|
const newCoinHashes = CoinHashesMap.updateWithCoins(secretKeys, wallet.coinHashes, CoinHashesMap.pickAllCoins(newState));
|
|
112
|
-
|
|
113
|
+
const updatedWallet = { ...wallet, state: newState, coinHashes: newCoinHashes };
|
|
114
|
+
return [updatedWallet, stateWithChanges.changes];
|
|
113
115
|
},
|
|
114
116
|
updateProgress(wallet, { appliedIndex, highestRelevantWalletIndex, highestIndex, highestRelevantIndex, isConnected, }) {
|
|
115
117
|
const updatedProgress = SyncProgress.createSyncProgress({
|
|
@@ -2,24 +2,24 @@ import type * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
|
2
2
|
import { Effect, Scope, Stream } from 'effect';
|
|
3
3
|
import { type WalletRuntimeError, type Variant, StateChange } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
|
|
4
4
|
import { type SerializationCapability } from './Serialization.js';
|
|
5
|
-
import { type EventsSyncUpdate, type SyncCapability, type SyncService } from './Sync.js';
|
|
5
|
+
import { type ChangesResult, type EventsSyncUpdate, type SyncCapability, type SyncService } from './Sync.js';
|
|
6
6
|
import { type TransactingCapability, type TokenTransfer, type BalancingResult } from './Transacting.js';
|
|
7
7
|
import { type WalletError } from './WalletError.js';
|
|
8
8
|
import { type CoinsAndBalancesCapability } from './CoinsAndBalances.js';
|
|
9
9
|
import { type KeysCapability } from './Keys.js';
|
|
10
10
|
import { type CoinSelection } from '@midnight-ntwrk/wallet-sdk-capabilities';
|
|
11
11
|
import { type CoreWallet } from './CoreWallet.js';
|
|
12
|
-
import { type
|
|
12
|
+
import { type TransactionHistoryService } from './TransactionHistory.js';
|
|
13
13
|
export declare namespace RunningV1Variant {
|
|
14
14
|
type Context<TSerialized, TSyncUpdate, TTransaction, TStartAux> = {
|
|
15
15
|
serializationCapability: SerializationCapability<CoreWallet, null, TSerialized>;
|
|
16
16
|
syncService: SyncService<CoreWallet, TStartAux, TSyncUpdate>;
|
|
17
|
-
syncCapability: SyncCapability<CoreWallet, TSyncUpdate>;
|
|
17
|
+
syncCapability: SyncCapability<CoreWallet, TSyncUpdate, ChangesResult>;
|
|
18
18
|
transactingCapability: TransactingCapability<ledger.ZswapSecretKeys, CoreWallet, TTransaction>;
|
|
19
19
|
coinsAndBalancesCapability: CoinsAndBalancesCapability<CoreWallet>;
|
|
20
20
|
keysCapability: KeysCapability<CoreWallet>;
|
|
21
21
|
coinSelection: CoinSelection<ledger.QualifiedShieldedCoinInfo>;
|
|
22
|
-
|
|
22
|
+
transactionHistoryService: TransactionHistoryService;
|
|
23
23
|
};
|
|
24
24
|
type AnyContext = Context<any, any, any, any>;
|
|
25
25
|
}
|
|
@@ -49,15 +49,16 @@ export class RunningV1Variant {
|
|
|
49
49
|
return this.startSync(startAux).pipe(Stream.runScoped(Sink.drain), Effect.forkScoped, Effect.provideService(Scope.Scope, this.#scope));
|
|
50
50
|
}
|
|
51
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
return pipe(SubscriptionRef.get(this.#context.stateRef), Stream.fromEffect, Stream.flatMap((state) => this.#v1Context.syncService.updates(state, startAux)), Stream.mapEffect((update) => SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => Effect.try({
|
|
53
|
+
try: () => {
|
|
54
|
+
const [newState, changesResult] = this.#v1Context.syncCapability.applyUpdate(state, update);
|
|
55
|
+
return [changesResult, newState];
|
|
56
|
+
},
|
|
57
|
+
catch: (err) => new OtherWalletError({
|
|
58
|
+
message: 'Error while applying sync update',
|
|
59
|
+
cause: err,
|
|
60
|
+
}),
|
|
61
|
+
})).pipe(Effect.flatMap(({ changes, protocolVersion }) => pipe(Effect.forEach(changes, (change) => pipe(this.#v1Context.transactionHistoryService.getTransactionDetails(change.source), Effect.flatMap((metadata) => this.#v1Context.transactionHistoryService.put(change, metadata, protocolVersion)), Effect.catchAllCause((cause) => Console.error('Error processing tx history metadata', cause))), { discard: true, concurrency: 'unbounded' }), Effect.forkScoped)), Effect.provideService(Scope.Scope, this.#scope))), Stream.tapError((error) => Console.error(error)), Stream.retry(pipe(Schedule.exponential(Duration.seconds(1), 2), Schedule.map((delay) => {
|
|
61
62
|
const maxDelay = Duration.minutes(2);
|
|
62
63
|
const jitter = Duration.millis(Math.floor(Math.random() * 1000));
|
|
63
64
|
const delayWithJitter = Duration.toMillis(delay) + Duration.toMillis(jitter);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Either } from 'effect';
|
|
2
2
|
import { WalletError } from './WalletError.js';
|
|
3
3
|
import { CoreWallet } from './CoreWallet.js';
|
|
4
|
-
import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
4
|
+
import { type NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
5
5
|
export type SerializationCapability<TWallet, TAux, TSerialized> = {
|
|
6
6
|
serialize(wallet: TWallet): TSerialized;
|
|
7
7
|
deserialize(aux: TAux, data: TSerialized): Either.Either<TWallet, WalletError>;
|
package/dist/v1/Sync.d.ts
CHANGED
|
@@ -1,46 +1,73 @@
|
|
|
1
1
|
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
2
|
-
import { Schema, Scope, Stream } from 'effect';
|
|
2
|
+
import { Schema, type Scope, Stream } from 'effect';
|
|
3
3
|
import { CoreWallet } from './CoreWallet.js';
|
|
4
|
-
import { Simulator, SimulatorState } from '
|
|
5
|
-
import { WalletError } from './WalletError.js';
|
|
6
|
-
import {
|
|
4
|
+
import { type Simulator, type SimulatorState } from '@midnight-ntwrk/wallet-sdk-capabilities/simulation';
|
|
5
|
+
import { type WalletError } from './WalletError.js';
|
|
6
|
+
import { type TransactionHistoryService } from './TransactionHistory.js';
|
|
7
7
|
export interface SyncService<TState, TStartAux, TUpdate> {
|
|
8
8
|
updates: (state: TState, auxData: TStartAux) => Stream.Stream<TUpdate, WalletError, Scope.Scope>;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
11
|
-
|
|
10
|
+
export type ChangesResult = {
|
|
11
|
+
readonly changes: ledger.ZswapStateChanges[];
|
|
12
|
+
readonly protocolVersion: number;
|
|
13
|
+
};
|
|
14
|
+
export interface SyncCapability<TState, TUpdate, TResult> {
|
|
15
|
+
applyUpdate: (state: TState, update: TUpdate) => [TState, TResult];
|
|
12
16
|
}
|
|
13
17
|
export type IndexerClientConnection = {
|
|
14
18
|
indexerHttpUrl: string;
|
|
15
19
|
indexerWsUrl?: string;
|
|
16
20
|
keepAlive?: number;
|
|
17
21
|
};
|
|
22
|
+
export type BatchUpdatesConfig = {
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of events to collect into a single batch before emitting.
|
|
25
|
+
*
|
|
26
|
+
* @default 10
|
|
27
|
+
*/
|
|
28
|
+
readonly size?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum time in milliseconds to wait for a full batch before emitting a partial one. Controls the `groupedWithin`
|
|
31
|
+
* timeout — lower values mean more responsive (but smaller) batches when events arrive slowly.
|
|
32
|
+
*
|
|
33
|
+
* @default 1
|
|
34
|
+
*/
|
|
35
|
+
readonly timeout?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Minimum delay in milliseconds injected between consecutive batches. Prevents the sync stream from saturating
|
|
38
|
+
* downstream consumers when many events are available at once. Set to 0 to disable spacing entirely.
|
|
39
|
+
*
|
|
40
|
+
* @default 4
|
|
41
|
+
*/
|
|
42
|
+
readonly spacing?: number;
|
|
43
|
+
};
|
|
18
44
|
export type DefaultSyncConfiguration = {
|
|
19
45
|
indexerClientConnection: IndexerClientConnection;
|
|
20
|
-
|
|
46
|
+
batchUpdates?: BatchUpdatesConfig;
|
|
21
47
|
};
|
|
22
48
|
export type DefaultSyncContext = {
|
|
23
|
-
|
|
49
|
+
transactionHistoryService: TransactionHistoryService;
|
|
24
50
|
};
|
|
25
|
-
type SecretKeysResource = <A>(cb: (keys: ledger.ZswapSecretKeys) => A) => A;
|
|
51
|
+
export type SecretKeysResource = <A>(cb: (keys: ledger.ZswapSecretKeys) => A) => A;
|
|
26
52
|
export declare const SecretKeysResource: {
|
|
27
53
|
create: (secretKeys: ledger.ZswapSecretKeys) => SecretKeysResource;
|
|
28
54
|
};
|
|
29
55
|
export type WalletSyncUpdate = {
|
|
30
56
|
updates: EventsSyncUpdate[];
|
|
31
|
-
secretKeys:
|
|
57
|
+
secretKeys: ledger.ZswapSecretKeys;
|
|
32
58
|
};
|
|
33
59
|
export declare const WalletSyncUpdate: {
|
|
34
60
|
create: (updates: EventsSyncUpdate[], secretKeys: ledger.ZswapSecretKeys) => WalletSyncUpdate;
|
|
35
61
|
};
|
|
36
62
|
export declare const EventsSyncUpdate: Schema.TaggedStruct<"EventsSyncUpdate", {
|
|
37
63
|
id: typeof Schema.Number;
|
|
64
|
+
protocolVersion: typeof Schema.Number;
|
|
38
65
|
maxId: typeof Schema.Number;
|
|
39
66
|
event: Schema.declare<ledger.Event, ledger.Event, readonly [], never>;
|
|
40
67
|
}>;
|
|
41
68
|
export type EventsSyncUpdate = Schema.Schema.Type<typeof EventsSyncUpdate>;
|
|
42
69
|
export declare const makeEventsSyncService: (config: DefaultSyncConfiguration) => SyncService<CoreWallet, ledger.ZswapSecretKeys, WalletSyncUpdate>;
|
|
43
|
-
export declare const makeEventsSyncCapability: () => SyncCapability<CoreWallet, WalletSyncUpdate>;
|
|
70
|
+
export declare const makeEventsSyncCapability: () => SyncCapability<CoreWallet, WalletSyncUpdate, ChangesResult>;
|
|
44
71
|
export type SimulatorSyncConfiguration = {
|
|
45
72
|
simulator: Simulator;
|
|
46
73
|
};
|
|
@@ -49,5 +76,4 @@ export type SimulatorSyncUpdate = {
|
|
|
49
76
|
secretKeys: ledger.ZswapSecretKeys;
|
|
50
77
|
};
|
|
51
78
|
export declare const makeSimulatorSyncService: (config: SimulatorSyncConfiguration) => SyncService<CoreWallet, ledger.ZswapSecretKeys, SimulatorSyncUpdate>;
|
|
52
|
-
export declare const makeSimulatorSyncCapability: () => SyncCapability<CoreWallet, SimulatorSyncUpdate>;
|
|
53
|
-
export {};
|
|
79
|
+
export declare const makeSimulatorSyncCapability: () => SyncCapability<CoreWallet, SimulatorSyncUpdate, ChangesResult>;
|
package/dist/v1/Sync.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
14
14
|
import { Chunk, Duration, Effect, Either, ParseResult, pipe, Schedule, Schema, Stream } from 'effect';
|
|
15
15
|
import { CoreWallet } from './CoreWallet.js';
|
|
16
|
+
import { getLastBlock, getBlockEventsFrom, } from '@midnight-ntwrk/wallet-sdk-capabilities/simulation';
|
|
16
17
|
import { ZswapEvents } from '@midnight-ntwrk/wallet-sdk-indexer-client';
|
|
17
18
|
import { ConnectionHelper, WsSubscriptionClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
|
|
18
19
|
import { SyncWalletError } from './WalletError.js';
|
|
@@ -23,17 +24,9 @@ const Uint8ArraySchema = Schema.declare((input) => input instanceof Uint8Array).
|
|
|
23
24
|
});
|
|
24
25
|
export const SecretKeysResource = {
|
|
25
26
|
create: (secretKeys) => {
|
|
26
|
-
/**
|
|
27
|
-
* TODO: future Ledger version will include `clear` function to clear the secret keys,
|
|
28
|
-
* it is intentend to be used here instead of `null`
|
|
29
|
-
*/
|
|
30
|
-
let sk = secretKeys;
|
|
31
27
|
return (cb) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
const result = cb(sk);
|
|
36
|
-
sk = null;
|
|
28
|
+
const result = cb(secretKeys);
|
|
29
|
+
secretKeys.clear();
|
|
37
30
|
return result;
|
|
38
31
|
};
|
|
39
32
|
},
|
|
@@ -42,7 +35,7 @@ export const WalletSyncUpdate = {
|
|
|
42
35
|
create: (updates, secretKeys) => {
|
|
43
36
|
return {
|
|
44
37
|
updates,
|
|
45
|
-
secretKeys
|
|
38
|
+
secretKeys,
|
|
46
39
|
};
|
|
47
40
|
},
|
|
48
41
|
};
|
|
@@ -63,10 +56,12 @@ const HexedLedgerEvent = pipe(Schema.Uint8ArrayFromHex, Schema.compose(LedgerEve
|
|
|
63
56
|
const EventsSyncUpdatePayload = Schema.Struct({
|
|
64
57
|
id: Schema.Number,
|
|
65
58
|
raw: Schema.String,
|
|
59
|
+
protocolVersion: Schema.Number,
|
|
66
60
|
maxId: Schema.Number,
|
|
67
61
|
});
|
|
68
62
|
export const EventsSyncUpdate = Schema.TaggedStruct('EventsSyncUpdate', {
|
|
69
63
|
id: Schema.Number,
|
|
64
|
+
protocolVersion: Schema.Number,
|
|
70
65
|
maxId: Schema.Number,
|
|
71
66
|
event: LedgerEventSchema,
|
|
72
67
|
});
|
|
@@ -74,12 +69,14 @@ const EventsSyncUpdateFromPayload = Schema.transformOrFail(EventsSyncUpdatePaylo
|
|
|
74
69
|
decode: (input) => pipe(Schema.decodeUnknownEither(HexedLedgerEvent)(input.raw), Either.map((event) => ({
|
|
75
70
|
_tag: 'EventsSyncUpdate',
|
|
76
71
|
id: input.id,
|
|
72
|
+
protocolVersion: input.protocolVersion,
|
|
77
73
|
maxId: input.maxId,
|
|
78
74
|
event,
|
|
79
75
|
})), Either.mapLeft((error) => new ParseResult.Unexpected(error, 'Failed to decode ledger event payload')), EitherOps.toEffect),
|
|
80
76
|
encode: (output) => pipe(Schema.encodeEither(HexedLedgerEvent)(output.event), Either.map((raw) => ({
|
|
81
77
|
id: output.id,
|
|
82
78
|
raw,
|
|
79
|
+
protocolVersion: output.protocolVersion,
|
|
83
80
|
maxId: output.maxId,
|
|
84
81
|
})), Either.mapLeft((error) => new ParseResult.Unexpected(error, 'Failed to encode ledger event payload')), EitherOps.toEffect),
|
|
85
82
|
});
|
|
@@ -97,8 +94,13 @@ export const makeEventsSyncService = (config) => {
|
|
|
97
94
|
}
|
|
98
95
|
const indexerWsUrl = indexerWsUrlResult.right;
|
|
99
96
|
const appliedIndex = state.progress?.appliedIndex ?? 0n;
|
|
100
|
-
const batchSize = config.
|
|
101
|
-
|
|
97
|
+
const batchSize = config.batchUpdates?.size ?? 10;
|
|
98
|
+
const batchTimeout = Duration.millis(config.batchUpdates?.timeout ?? 1);
|
|
99
|
+
const batchSpacing = config.batchUpdates?.spacing ?? 4;
|
|
100
|
+
const eventsStream = pipe(ZswapEvents.run({ id: Number(appliedIndex) }), Stream.provideLayer(WsSubscriptionClient.layer({ url: indexerWsUrl, keepAlive: config.indexerClientConnection.keepAlive })), Stream.mapError((error) => new SyncWalletError(error)), Stream.mapEffect((subscription) => pipe(subscription.zswapLedgerEvents, Schema.decodeUnknownEither(EventsSyncUpdateFromPayload), Either.mapLeft((err) => new SyncWalletError(err)), EitherOps.toEffect)), Stream.groupedWithin(batchSize, batchTimeout), Stream.map(Chunk.toArray), Stream.map((data) => WalletSyncUpdate.create(data, secretKeys)));
|
|
101
|
+
return batchSpacing > 0
|
|
102
|
+
? Stream.schedule(eventsStream, Schedule.spaced(Duration.millis(batchSpacing)))
|
|
103
|
+
: eventsStream;
|
|
102
104
|
},
|
|
103
105
|
};
|
|
104
106
|
};
|
|
@@ -106,7 +108,7 @@ export const makeEventsSyncCapability = () => {
|
|
|
106
108
|
return {
|
|
107
109
|
applyUpdate: (state, wrappedUpdate) => {
|
|
108
110
|
if (wrappedUpdate.updates.length === 0) {
|
|
109
|
-
return state;
|
|
111
|
+
return [state, { changes: [], protocolVersion: Number(state.protocolVersion) }];
|
|
110
112
|
}
|
|
111
113
|
const lastUpdate = wrappedUpdate.updates.at(-1);
|
|
112
114
|
const nextIndex = BigInt(lastUpdate.id);
|
|
@@ -114,31 +116,67 @@ export const makeEventsSyncCapability = () => {
|
|
|
114
116
|
// in case the nextIndex is less than or equal to the appliedIndex
|
|
115
117
|
// just update highestRelevantWalletIndex
|
|
116
118
|
if (nextIndex <= state.progress.appliedIndex) {
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
return [
|
|
120
|
+
CoreWallet.updateProgress(state, {
|
|
121
|
+
highestRelevantWalletIndex,
|
|
122
|
+
isConnected: true,
|
|
123
|
+
}),
|
|
124
|
+
{ changes: [], protocolVersion: lastUpdate.protocolVersion },
|
|
125
|
+
];
|
|
121
126
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
127
|
+
const [newState, newChanges] = CoreWallet.replayEventsWithChanges(state, wrappedUpdate.secretKeys, wrappedUpdate.updates.map((u) => u.event));
|
|
128
|
+
const updatedState = CoreWallet.updateProgress(newState, {
|
|
129
|
+
highestRelevantWalletIndex,
|
|
130
|
+
appliedIndex: nextIndex,
|
|
131
|
+
isConnected: true,
|
|
128
132
|
});
|
|
133
|
+
return [updatedState, { changes: newChanges, protocolVersion: lastUpdate.protocolVersion }];
|
|
129
134
|
},
|
|
130
135
|
};
|
|
131
136
|
};
|
|
132
137
|
export const makeSimulatorSyncService = (config) => {
|
|
133
138
|
return {
|
|
134
|
-
updates: (_state, secretKeys) =>
|
|
139
|
+
updates: (_state, secretKeys) => {
|
|
140
|
+
// Get the initial state immediately to ensure we process the genesis block.
|
|
141
|
+
// Then subscribe to state$ for subsequent changes, but deduplicate by block number
|
|
142
|
+
// to avoid processing the same block twice.
|
|
143
|
+
let lastSeenBlockNumber;
|
|
144
|
+
return pipe(Stream.fromEffect(config.simulator.getLatestState()), Stream.concat(config.simulator.state$), Stream.filter((state) => {
|
|
145
|
+
const lastBlock = getLastBlock(state);
|
|
146
|
+
if (lastBlock === undefined) {
|
|
147
|
+
return false; // Skip blank state
|
|
148
|
+
}
|
|
149
|
+
const blockNumber = lastBlock.number;
|
|
150
|
+
// Skip if we've already seen this block (deduplication)
|
|
151
|
+
if (lastSeenBlockNumber !== undefined && blockNumber <= lastSeenBlockNumber) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
lastSeenBlockNumber = blockNumber;
|
|
155
|
+
return true;
|
|
156
|
+
}), Stream.map((state) => ({ update: state, secretKeys })));
|
|
157
|
+
},
|
|
135
158
|
};
|
|
136
159
|
};
|
|
137
160
|
export const makeSimulatorSyncCapability = () => {
|
|
138
161
|
return {
|
|
139
162
|
applyUpdate: (state, update) => {
|
|
140
|
-
const { update:
|
|
141
|
-
|
|
163
|
+
const { update: simulatorState, secretKeys } = update;
|
|
164
|
+
const lastBlock = getLastBlock(simulatorState);
|
|
165
|
+
if (lastBlock === undefined) {
|
|
166
|
+
return [state, { changes: [], protocolVersion: Number(state.protocolVersion) }];
|
|
167
|
+
}
|
|
168
|
+
// Get all events from blocks starting at appliedIndex (the next block to process).
|
|
169
|
+
// appliedIndex semantics: the first block number we haven't processed yet.
|
|
170
|
+
// Initial: appliedIndex = 0 (haven't processed any blocks)
|
|
171
|
+
// After processing block N: appliedIndex = N + 1 (next block to process)
|
|
172
|
+
const events = [...getBlockEventsFrom(simulatorState, state.progress.appliedIndex)];
|
|
173
|
+
const [newState, newChanges] = CoreWallet.replayEventsWithChanges(state, secretKeys, events);
|
|
174
|
+
return [
|
|
175
|
+
CoreWallet.updateProgress(newState, {
|
|
176
|
+
appliedIndex: lastBlock.number + 1n,
|
|
177
|
+
}),
|
|
178
|
+
{ changes: newChanges, protocolVersion: Number(state.protocolVersion) },
|
|
179
|
+
];
|
|
142
180
|
},
|
|
143
181
|
};
|
|
144
182
|
};
|
package/dist/v1/Transacting.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
2
|
-
import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
2
|
+
import { type NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
3
3
|
import { Array as Arr, Either } from 'effect';
|
|
4
4
|
import { CoreWallet } from './CoreWallet.js';
|
|
5
|
-
import { WalletError } from './WalletError.js';
|
|
6
|
-
import { CoinSelection } from '@midnight-ntwrk/wallet-sdk-capabilities';
|
|
7
|
-
import { ShieldedAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
|
|
5
|
+
import { type WalletError } from './WalletError.js';
|
|
6
|
+
import { type CoinSelection } from '@midnight-ntwrk/wallet-sdk-capabilities';
|
|
7
|
+
import { type ShieldedAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
|
|
8
8
|
import { TransactionImbalances } from './TransactionImbalances.js';
|
|
9
9
|
import { TransactionOps } from './TransactionOps.js';
|
|
10
|
-
import { CoinsAndBalancesCapability } from './CoinsAndBalances.js';
|
|
11
|
-
import { KeysCapability } from './Keys.js';
|
|
10
|
+
import { type CoinsAndBalancesCapability } from './CoinsAndBalances.js';
|
|
11
|
+
import { type KeysCapability } from './Keys.js';
|
|
12
12
|
export interface TokenTransfer {
|
|
13
13
|
readonly amount: bigint;
|
|
14
14
|
readonly type: ledger.RawTokenType;
|
|
@@ -44,7 +44,7 @@ export declare class TransactingCapabilityImplementation<TTransaction extends le
|
|
|
44
44
|
initSwap(secretKeys: ledger.ZswapSecretKeys, state: CoreWallet, desiredInputs: Record<ledger.RawTokenType, bigint>, desiredOutputs: ReadonlyArray<TokenTransfer>): Either.Either<[ledger.UnprovenTransaction, CoreWallet], WalletError>;
|
|
45
45
|
revertTransaction(state: CoreWallet, transaction: TTransaction | ledger.UnprovenTransaction): Either.Either<CoreWallet, WalletError>;
|
|
46
46
|
balanceFallibleSection(secretKeys: ledger.ZswapSecretKeys, state: CoreWallet, imbalances: TransactionImbalances, coinSelection: CoinSelection<ledger.QualifiedShieldedCoinInfo>): Either.Either<{
|
|
47
|
-
|
|
47
|
+
fallibleOffers: Map<number, ledger.ZswapOffer<ledger.PreProof>>;
|
|
48
48
|
newState: CoreWallet;
|
|
49
49
|
}, WalletError>;
|
|
50
50
|
}
|
package/dist/v1/Transacting.js
CHANGED
|
@@ -44,9 +44,12 @@ export class TransactingCapabilityImplementation {
|
|
|
44
44
|
if (TransactionImbalances.areBalanced(initialImbalances)) {
|
|
45
45
|
return [undefined, state];
|
|
46
46
|
}
|
|
47
|
-
const { newState: afterFallible,
|
|
47
|
+
const { newState: afterFallible, fallibleOffers } = yield* this.balanceFallibleSection(secretKeys, state, initialImbalances, coinSelection);
|
|
48
48
|
const { newState: afterGuaranteed, offer: guaranteed } = yield* this.#balanceGuaranteedSection(secretKeys, afterFallible, initialImbalances, coinSelection, Imbalances.empty());
|
|
49
|
-
|
|
49
|
+
const balancedTx = ledger.Transaction.fromParts(this.networkId, guaranteed);
|
|
50
|
+
// workaround as Transaction.fromParts does not accept fallible offers with specific segments
|
|
51
|
+
balancedTx.fallibleOffer = fallibleOffers.size > 0 ? fallibleOffers : undefined;
|
|
52
|
+
return [balancedTx, afterGuaranteed];
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
55
|
makeTransfer(secretKeys, state, transfers) {
|
|
@@ -120,43 +123,44 @@ export class TransactingCapabilityImplementation {
|
|
|
120
123
|
}), Option.map((offer) => ({ offer, newState: stateAfterWatches })));
|
|
121
124
|
}
|
|
122
125
|
balanceFallibleSection(secretKeys, state, imbalances, coinSelection) {
|
|
123
|
-
return Either.try({
|
|
126
|
+
return Array.from(imbalances.fallible.entries()).reduce((acc, [segment, segmentImbalances]) => Either.flatMap(acc, ({ fallibleOffers, newState }) => pipe(Either.try({
|
|
124
127
|
try: () => {
|
|
125
128
|
const fallibleBalanceRecipe = getBalanceRecipe({
|
|
126
129
|
coins: this.getCoins()
|
|
127
|
-
.getAvailableCoins(
|
|
130
|
+
.getAvailableCoins(newState)
|
|
128
131
|
.map((c) => c.coin),
|
|
129
|
-
initialImbalances:
|
|
132
|
+
initialImbalances: segmentImbalances,
|
|
130
133
|
transactionCostModel: ShieldedCostModel,
|
|
131
134
|
feeTokenType: '',
|
|
132
135
|
coinSelection,
|
|
133
136
|
createOutput: (coin) => ledger.createShieldedCoinInfo(coin.type, coin.value),
|
|
134
137
|
isCoinEqual: (a, b) => a.type === b.type && a.value === b.value,
|
|
135
138
|
});
|
|
136
|
-
return
|
|
137
|
-
onNone: () => ({
|
|
138
|
-
newState: state,
|
|
139
|
-
offer: undefined,
|
|
140
|
-
}),
|
|
141
|
-
onSome: (res) => res,
|
|
142
|
-
}));
|
|
139
|
+
return this.#prepareOffer(secretKeys, newState, fallibleBalanceRecipe, segment);
|
|
143
140
|
},
|
|
144
141
|
catch: (err) => {
|
|
145
142
|
if (err instanceof BalancingInsufficientFundsError) {
|
|
146
143
|
return new InsufficientFundsError({
|
|
147
|
-
message:
|
|
144
|
+
message: `Insufficient funds for fallible segment ${segment}`,
|
|
148
145
|
tokenType: err.tokenType,
|
|
149
|
-
amount:
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
return new OtherWalletError({
|
|
154
|
-
message: 'Balancing fallible section failed',
|
|
155
|
-
cause: err,
|
|
146
|
+
amount: segmentImbalances.get(err.tokenType) ?? 0n,
|
|
156
147
|
});
|
|
157
148
|
}
|
|
149
|
+
return new OtherWalletError({
|
|
150
|
+
message: `Balancing fallible segment ${segment} failed`,
|
|
151
|
+
cause: err,
|
|
152
|
+
});
|
|
158
153
|
},
|
|
159
|
-
})
|
|
154
|
+
}), Either.map(Option.match({
|
|
155
|
+
onNone: () => ({ fallibleOffers, newState }),
|
|
156
|
+
onSome: (res) => ({
|
|
157
|
+
fallibleOffers: new Map([...fallibleOffers, [segment, res.offer]]),
|
|
158
|
+
newState: res.newState,
|
|
159
|
+
}),
|
|
160
|
+
})))), Either.right({
|
|
161
|
+
fallibleOffers: new Map(),
|
|
162
|
+
newState: state,
|
|
163
|
+
}));
|
|
160
164
|
}
|
|
161
165
|
#balanceGuaranteedSection(secretKeys, state, imbalances, coinSelection, targetImbalances) {
|
|
162
166
|
return Either.gen(this, function* () {
|