@midnightntwrk/wallet-sdk-shielded 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -0
- package/dist/ShieldedWallet.d.ts +65 -0
- package/dist/ShieldedWallet.js +132 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/v1/CoinsAndBalances.d.ts +24 -0
- package/dist/v1/CoinsAndBalances.js +46 -0
- package/dist/v1/CoreWallet.d.ts +47 -0
- package/dist/v1/CoreWallet.js +153 -0
- package/dist/v1/Keys.d.ts +8 -0
- package/dist/v1/Keys.js +16 -0
- package/dist/v1/RunningV1Variant.d.ts +40 -0
- package/dist/v1/RunningV1Variant.js +98 -0
- package/dist/v1/Serialization.d.ts +12 -0
- package/dist/v1/Serialization.js +79 -0
- package/dist/v1/Sync.d.ts +83 -0
- package/dist/v1/Sync.js +206 -0
- package/dist/v1/Transacting.d.ts +50 -0
- package/dist/v1/Transacting.js +258 -0
- package/dist/v1/TransactionHistory.d.ts +64 -0
- package/dist/v1/TransactionHistory.js +98 -0
- package/dist/v1/TransactionImbalances.d.ts +10 -0
- package/dist/v1/TransactionImbalances.js +30 -0
- package/dist/v1/TransactionOps.d.ts +17 -0
- package/dist/v1/TransactionOps.js +73 -0
- package/dist/v1/V1Builder.d.ts +93 -0
- package/dist/v1/V1Builder.js +172 -0
- package/dist/v1/WalletError.d.ts +74 -0
- package/dist/v1/WalletError.js +53 -0
- package/dist/v1/index.d.ts +12 -0
- package/dist/v1/index.js +24 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# @midnightntwrk/wallet-sdk-shielded
|
|
2
|
+
|
|
3
|
+
Manages shielded tokens on the Midnight network using zero-knowledge proofs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @midnightntwrk/wallet-sdk-shielded
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The Shielded Wallet handles private token operations where transaction values and addresses are hidden from observers
|
|
14
|
+
while maintaining verifiability. It provides:
|
|
15
|
+
|
|
16
|
+
- Zero-knowledge proof generation
|
|
17
|
+
- Coin commitment tracking
|
|
18
|
+
- Encrypted output decryption
|
|
19
|
+
- Shielded transfer transactions
|
|
20
|
+
- Transaction balancing for shielded tokens
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Starting the Wallet
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { ShieldedWallet } from '@midnightntwrk/wallet-sdk-shielded';
|
|
28
|
+
import * as ledger from '@midnight-ntwrk/ledger-v7';
|
|
29
|
+
import { randomBytes } from 'node:crypto';
|
|
30
|
+
|
|
31
|
+
// Configuration for the wallet
|
|
32
|
+
const configuration = {
|
|
33
|
+
networkId: 'preview',
|
|
34
|
+
provingServerUrl: new URL('http://localhost:6300'),
|
|
35
|
+
relayURL: new URL('ws://localhost:9944'),
|
|
36
|
+
indexerClientConnection: {
|
|
37
|
+
indexerHttpUrl: 'http://localhost:8088/api/v3/graphql',
|
|
38
|
+
indexerWsUrl: 'ws://localhost:8088/api/v3/graphql/ws',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Create secret keys from a shielded seed
|
|
43
|
+
const seed = randomBytes(32);
|
|
44
|
+
const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(seed);
|
|
45
|
+
|
|
46
|
+
// Create and start the wallet
|
|
47
|
+
const shieldedWallet = ShieldedWallet(configuration).startWithSecretKeys(shieldedSecretKeys);
|
|
48
|
+
|
|
49
|
+
// Start syncing with the network
|
|
50
|
+
await shieldedWallet.start(shieldedSecretKeys);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Alternative: Start with Seed Directly
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Start directly with a shielded seed
|
|
57
|
+
const shieldedWallet = ShieldedWallet(configuration).startWithSeed(seed);
|
|
58
|
+
await shieldedWallet.start(ledger.ZswapSecretKeys.fromSeed(seed));
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Restoring from Serialized State
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// Restore a wallet from previously serialized state
|
|
65
|
+
const shieldedWallet = ShieldedWallet(configuration).restore(serializedState);
|
|
66
|
+
await shieldedWallet.start(shieldedSecretKeys);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Observing State
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
wallet.state.subscribe((state) => {
|
|
73
|
+
console.log('Progress:', state.state.progress);
|
|
74
|
+
console.log('Coins:', state.state.coins);
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Creating Transfer Transactions
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const tx = await wallet.transferTransaction(shieldedSecretKeys, [
|
|
82
|
+
{ type: 'NIGHT', receiverAddress: 'mn_shield-addr1...', amount: 1000n },
|
|
83
|
+
]);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Balancing Transactions
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const balancingTx = await wallet.balanceTransaction(shieldedSecretKeys, transactionToBalance);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Creating Swap Offers
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const swapTx = await wallet.initSwap(
|
|
96
|
+
shieldedSecretKeys,
|
|
97
|
+
{ NIGHT: 500n }, // inputs
|
|
98
|
+
[{ type: 'TOKEN_A', receiverAddress: shieldedAddress, amount: 100n }], // outputs
|
|
99
|
+
);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Privacy Model
|
|
103
|
+
|
|
104
|
+
Shielded transactions use zero-knowledge proofs to hide:
|
|
105
|
+
|
|
106
|
+
- Transaction amounts
|
|
107
|
+
- Sender addresses
|
|
108
|
+
- Receiver addresses
|
|
109
|
+
|
|
110
|
+
While still proving:
|
|
111
|
+
|
|
112
|
+
- The sender has sufficient balance
|
|
113
|
+
- No tokens are created or destroyed
|
|
114
|
+
- The transaction is valid
|
|
115
|
+
|
|
116
|
+
## Exports
|
|
117
|
+
|
|
118
|
+
- `ShieldedWallet` - Main wallet class
|
|
119
|
+
- `ShieldedWalletState` - Wallet state type
|
|
120
|
+
- Version 1 exports via `@midnightntwrk/wallet-sdk-shielded/v1`
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
Apache-2.0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type ProtocolState, ProtocolVersion, type SyncProgress } from '@midnightntwrk/wallet-sdk-abstractions';
|
|
2
|
+
import { type BaseV1Configuration, type DefaultV1Configuration, type V1Variant, CoreWallet } from './v1/index.js';
|
|
3
|
+
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
4
|
+
import * as rx from 'rxjs';
|
|
5
|
+
import { type BalancingResult } from './v1/Transacting.js';
|
|
6
|
+
import { type SerializationCapability } from './v1/Serialization.js';
|
|
7
|
+
import { type TransactionHistoryService } from './v1/TransactionHistory.js';
|
|
8
|
+
import { type AvailableCoin, type CoinsAndBalancesCapability, type PendingCoin } from './v1/CoinsAndBalances.js';
|
|
9
|
+
import { type KeysCapability } from './v1/Keys.js';
|
|
10
|
+
import { type ShieldedAddress, type ShieldedCoinPublicKey, type ShieldedEncryptionPublicKey } from '@midnightntwrk/wallet-sdk-address-format';
|
|
11
|
+
import { type TokenTransfer } from './v1/Transacting.js';
|
|
12
|
+
import { type WalletSyncUpdate } from './v1/Sync.js';
|
|
13
|
+
import { type Variant, type VariantBuilder, type WalletLike } from '@midnightntwrk/wallet-sdk-runtime/abstractions';
|
|
14
|
+
export type ShieldedWalletCapabilities<TSerialized = string> = {
|
|
15
|
+
serialization: SerializationCapability<CoreWallet, null, TSerialized>;
|
|
16
|
+
coinsAndBalances: CoinsAndBalancesCapability<CoreWallet>;
|
|
17
|
+
keys: KeysCapability<CoreWallet>;
|
|
18
|
+
};
|
|
19
|
+
export type ShieldedWalletServices = {
|
|
20
|
+
transactionHistory: TransactionHistoryService;
|
|
21
|
+
};
|
|
22
|
+
export type UnboundTransaction = ledger.Transaction<ledger.SignatureEnabled, ledger.Proof, ledger.PreBinding>;
|
|
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>;
|
|
25
|
+
readonly protocolVersion: ProtocolVersion.ProtocolVersion;
|
|
26
|
+
readonly state: CoreWallet;
|
|
27
|
+
readonly capabilities: ShieldedWalletCapabilities<TSerialized>;
|
|
28
|
+
readonly services: ShieldedWalletServices;
|
|
29
|
+
get balances(): Record<ledger.RawTokenType, bigint>;
|
|
30
|
+
get totalCoins(): readonly (AvailableCoin | PendingCoin)[];
|
|
31
|
+
get availableCoins(): readonly AvailableCoin[];
|
|
32
|
+
get pendingCoins(): readonly PendingCoin[];
|
|
33
|
+
get coinPublicKey(): ShieldedCoinPublicKey;
|
|
34
|
+
get encryptionPublicKey(): ShieldedEncryptionPublicKey;
|
|
35
|
+
get address(): ShieldedAddress;
|
|
36
|
+
get progress(): SyncProgress.SyncProgress;
|
|
37
|
+
constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: ShieldedWalletCapabilities<TSerialized>, services: ShieldedWalletServices);
|
|
38
|
+
serialize(): TSerialized;
|
|
39
|
+
}
|
|
40
|
+
export type ShieldedWallet = CustomizedShieldedWallet<ledger.ZswapSecretKeys, ledger.FinalizedTransaction, WalletSyncUpdate, string>;
|
|
41
|
+
export type ShieldedWalletClass = CustomizedShieldedWalletClass<ledger.ZswapSecretKeys, ledger.FinalizedTransaction, WalletSyncUpdate, string>;
|
|
42
|
+
export type ShieldedWalletAPI<TStartAux = ledger.ZswapSecretKeys, TTransaction = ledger.FinalizedTransaction, TSerialized = string> = {
|
|
43
|
+
readonly state: rx.Observable<ShieldedWalletState<TSerialized, TTransaction>>;
|
|
44
|
+
start(secretKeys: TStartAux): Promise<void>;
|
|
45
|
+
balanceTransaction(secretKeys: ledger.ZswapSecretKeys, tx: ledger.Transaction<ledger.Signaturish, ledger.Proofish, ledger.Bindingish>): Promise<BalancingResult>;
|
|
46
|
+
transferTransaction(secretKeys: ledger.ZswapSecretKeys, outputs: readonly TokenTransfer[]): Promise<ledger.UnprovenTransaction>;
|
|
47
|
+
initSwap(secretKeys: ledger.ZswapSecretKeys, desiredInputs: Record<ledger.RawTokenType, bigint>, desiredOutputs: readonly TokenTransfer[]): Promise<ledger.UnprovenTransaction>;
|
|
48
|
+
serializeState(): Promise<TSerialized>;
|
|
49
|
+
waitForSyncedState(allowedGap?: bigint): Promise<ShieldedWalletState<TSerialized, TTransaction>>;
|
|
50
|
+
getAddress(): Promise<ShieldedAddress>;
|
|
51
|
+
revertTransaction(transaction: ledger.Transaction<ledger.Signaturish, ledger.Proofish, ledger.Bindingish>): Promise<void>;
|
|
52
|
+
stop(): Promise<void>;
|
|
53
|
+
};
|
|
54
|
+
export type CustomizedShieldedWallet<TStartAux = ledger.ZswapSecretKeys, TTransaction = ledger.FinalizedTransaction, TSyncUpdate = WalletSyncUpdate, TSerialized = string> = ShieldedWalletAPI<TStartAux, TTransaction, TSerialized> & WalletLike.WalletLike<[Variant.VersionedVariant<V1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux>>]>;
|
|
55
|
+
export type DefaultShieldedConfiguration = DefaultV1Configuration;
|
|
56
|
+
export interface CustomizedShieldedWalletClass<TStartAux = ledger.ZswapSecretKeys, TTransaction = ledger.FinalizedTransaction, TSyncUpdate = WalletSyncUpdate, TSerialized = string, TConfig extends BaseV1Configuration = DefaultShieldedConfiguration> extends WalletLike.BaseWalletClass<[
|
|
57
|
+
Variant.VersionedVariant<V1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux>>
|
|
58
|
+
]> {
|
|
59
|
+
configuration: TConfig;
|
|
60
|
+
startWithSeed(seed: Uint8Array): CustomizedShieldedWallet<TStartAux, TTransaction, TSyncUpdate, TSerialized>;
|
|
61
|
+
startWithSecretKeys(secretKeys: ledger.ZswapSecretKeys): CustomizedShieldedWallet<TStartAux, TTransaction, TSyncUpdate, TSerialized>;
|
|
62
|
+
restore(serializedState: TSerialized): CustomizedShieldedWallet<TStartAux, TTransaction, TSyncUpdate, TSerialized>;
|
|
63
|
+
}
|
|
64
|
+
export declare function ShieldedWallet(configuration: DefaultV1Configuration): ShieldedWalletClass;
|
|
65
|
+
export declare function CustomShieldedWallet<TConfig extends BaseV1Configuration = DefaultV1Configuration, TStartAux = ledger.ZswapSecretKeys, TTransaction = ledger.FinalizedTransaction, TSyncUpdate = WalletSyncUpdate, TSerialized = string>(configuration: TConfig, builder: VariantBuilder.VariantBuilder<V1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux>, TConfig>): CustomizedShieldedWalletClass<TStartAux, TTransaction, TSyncUpdate, TSerialized, TConfig>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 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 { ProtocolVersion } from '@midnightntwrk/wallet-sdk-abstractions';
|
|
14
|
+
import { V1Builder, V1Tag, CoreWallet, } from './v1/index.js';
|
|
15
|
+
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
16
|
+
import { Effect, Either } from 'effect';
|
|
17
|
+
import * as rx from 'rxjs';
|
|
18
|
+
import { WalletBuilder } from '@midnightntwrk/wallet-sdk-runtime';
|
|
19
|
+
export class ShieldedWalletState {
|
|
20
|
+
static mapState = (variant) => (state) => {
|
|
21
|
+
const { serialization, coinsAndBalances, keys } = variant;
|
|
22
|
+
const { transactionHistory } = variant;
|
|
23
|
+
return new ShieldedWalletState(state, { serialization, coinsAndBalances, keys }, { transactionHistory });
|
|
24
|
+
};
|
|
25
|
+
protocolVersion;
|
|
26
|
+
state;
|
|
27
|
+
capabilities;
|
|
28
|
+
services;
|
|
29
|
+
get balances() {
|
|
30
|
+
return this.capabilities.coinsAndBalances.getAvailableBalances(this.state);
|
|
31
|
+
}
|
|
32
|
+
get totalCoins() {
|
|
33
|
+
return this.capabilities.coinsAndBalances.getTotalCoins(this.state);
|
|
34
|
+
}
|
|
35
|
+
get availableCoins() {
|
|
36
|
+
return this.capabilities.coinsAndBalances.getAvailableCoins(this.state);
|
|
37
|
+
}
|
|
38
|
+
get pendingCoins() {
|
|
39
|
+
return this.capabilities.coinsAndBalances.getPendingCoins(this.state);
|
|
40
|
+
}
|
|
41
|
+
get coinPublicKey() {
|
|
42
|
+
return this.capabilities.keys.getCoinPublicKey(this.state);
|
|
43
|
+
}
|
|
44
|
+
get encryptionPublicKey() {
|
|
45
|
+
return this.capabilities.keys.getEncryptionPublicKey(this.state);
|
|
46
|
+
}
|
|
47
|
+
get address() {
|
|
48
|
+
return this.capabilities.keys.getAddress(this.state);
|
|
49
|
+
}
|
|
50
|
+
get progress() {
|
|
51
|
+
return this.state.progress;
|
|
52
|
+
}
|
|
53
|
+
constructor(state, capabilities, services) {
|
|
54
|
+
this.protocolVersion = state.version;
|
|
55
|
+
this.state = state.state;
|
|
56
|
+
this.capabilities = capabilities;
|
|
57
|
+
this.services = services;
|
|
58
|
+
}
|
|
59
|
+
serialize() {
|
|
60
|
+
return this.capabilities.serialization.serialize(this.state);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function ShieldedWallet(configuration) {
|
|
64
|
+
return CustomShieldedWallet(configuration, new V1Builder().withDefaults());
|
|
65
|
+
}
|
|
66
|
+
export function CustomShieldedWallet(configuration, builder) {
|
|
67
|
+
const buildArgs = [configuration];
|
|
68
|
+
const BaseWallet = WalletBuilder.init()
|
|
69
|
+
.withVariant(ProtocolVersion.MinSupportedVersion, builder)
|
|
70
|
+
.build(...buildArgs);
|
|
71
|
+
return class CustomShieldedWalletImplementation extends BaseWallet {
|
|
72
|
+
static startWithSecretKeys(secretKeys) {
|
|
73
|
+
return CustomShieldedWalletImplementation.startFirst(CustomShieldedWalletImplementation, CoreWallet.initEmpty(secretKeys, CustomShieldedWalletImplementation.configuration.networkId));
|
|
74
|
+
}
|
|
75
|
+
static startWithSeed(seed) {
|
|
76
|
+
const secretKeys = ledger.ZswapSecretKeys.fromSeed(seed);
|
|
77
|
+
return CustomShieldedWalletImplementation.startWithSecretKeys(secretKeys);
|
|
78
|
+
}
|
|
79
|
+
static restore(serializedState) {
|
|
80
|
+
const deserialized = CustomShieldedWalletImplementation.allVariantsRecord()[V1Tag].variant.deserializeState(serializedState)
|
|
81
|
+
.pipe(Either.getOrThrow);
|
|
82
|
+
return CustomShieldedWalletImplementation.startFirst(CustomShieldedWalletImplementation, deserialized);
|
|
83
|
+
}
|
|
84
|
+
state;
|
|
85
|
+
constructor(runtime, scope) {
|
|
86
|
+
super(runtime, scope);
|
|
87
|
+
this.state = this.rawState.pipe(rx.map(ShieldedWalletState.mapState(CustomShieldedWalletImplementation.allVariantsRecord()[V1Tag].variant)), rx.shareReplay({ refCount: true, bufferSize: 1 }));
|
|
88
|
+
}
|
|
89
|
+
start(secretKeys) {
|
|
90
|
+
return this.runtime.dispatch({ [V1Tag]: (v1) => v1.startSyncInBackground(secretKeys) }).pipe(Effect.runPromise);
|
|
91
|
+
}
|
|
92
|
+
balanceTransaction(secretKeys, tx) {
|
|
93
|
+
return this.runtime
|
|
94
|
+
.dispatch({
|
|
95
|
+
[V1Tag]: (v1) => v1.balanceTransaction(secretKeys, tx),
|
|
96
|
+
})
|
|
97
|
+
.pipe(Effect.runPromise);
|
|
98
|
+
}
|
|
99
|
+
transferTransaction(secretKeys, outputs) {
|
|
100
|
+
return this.runtime
|
|
101
|
+
.dispatch({
|
|
102
|
+
[V1Tag]: (v1) => v1.transferTransaction(secretKeys, outputs),
|
|
103
|
+
})
|
|
104
|
+
.pipe(Effect.runPromise);
|
|
105
|
+
}
|
|
106
|
+
initSwap(secretKeys, desiredInputs, desiredOutputs) {
|
|
107
|
+
return this.runtime
|
|
108
|
+
.dispatch({ [V1Tag]: (v1) => v1.initSwap(secretKeys, desiredInputs, desiredOutputs) })
|
|
109
|
+
.pipe(Effect.runPromise);
|
|
110
|
+
}
|
|
111
|
+
revertTransaction(transaction) {
|
|
112
|
+
return this.runtime
|
|
113
|
+
.dispatch({
|
|
114
|
+
[V1Tag]: (v1) => v1.revertTransaction(transaction),
|
|
115
|
+
})
|
|
116
|
+
.pipe(Effect.runPromise);
|
|
117
|
+
}
|
|
118
|
+
waitForSyncedState(allowedGap = 0n) {
|
|
119
|
+
return rx.firstValueFrom(this.state.pipe(rx.filter((state) => state.state.progress.isCompleteWithin(allowedGap))));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Serializes the most recent state It's preferable to use [[ShieldedWalletState.serialize]] instead, to know
|
|
123
|
+
* exactly, which state is serialized
|
|
124
|
+
*/
|
|
125
|
+
serializeState() {
|
|
126
|
+
return rx.firstValueFrom(this.state).then((state) => state.serialize());
|
|
127
|
+
}
|
|
128
|
+
getAddress() {
|
|
129
|
+
return rx.firstValueFrom(this.state).then((state) => state.address);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 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
|
+
export * from './ShieldedWallet.js';
|
|
14
|
+
export { ShieldedSectionSchema, mergeShieldedSections, } from './v1/TransactionHistory.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CoreWallet } from './CoreWallet.js';
|
|
2
|
+
import type * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
3
|
+
export type AvailableCoin = {
|
|
4
|
+
coin: ledger.QualifiedShieldedCoinInfo;
|
|
5
|
+
commitment: ledger.CoinCommitment;
|
|
6
|
+
nullifier: ledger.Nullifier;
|
|
7
|
+
};
|
|
8
|
+
export type PendingCoin = {
|
|
9
|
+
coin: ledger.ShieldedCoinInfo;
|
|
10
|
+
ttl: Date | undefined;
|
|
11
|
+
commitment: ledger.CoinCommitment;
|
|
12
|
+
nullifier: ledger.Nullifier;
|
|
13
|
+
};
|
|
14
|
+
export type Coin = AvailableCoin | PendingCoin;
|
|
15
|
+
export type Balances = Record<ledger.RawTokenType, bigint>;
|
|
16
|
+
export type CoinsAndBalancesCapability<TState> = {
|
|
17
|
+
getAvailableBalances(state: TState): Balances;
|
|
18
|
+
getPendingBalances(state: TState): Balances;
|
|
19
|
+
getTotalBalances(state: TState): Balances;
|
|
20
|
+
getAvailableCoins(state: TState): readonly AvailableCoin[];
|
|
21
|
+
getPendingCoins(state: TState): readonly PendingCoin[];
|
|
22
|
+
getTotalCoins(state: TState): ReadonlyArray<AvailableCoin | PendingCoin>;
|
|
23
|
+
};
|
|
24
|
+
export declare const makeDefaultCoinsAndBalancesCapability: () => CoinsAndBalancesCapability<CoreWallet>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { pipe, Array } from 'effect';
|
|
2
|
+
import { RecordOps } from '@midnightntwrk/wallet-sdk-utilities';
|
|
3
|
+
const calculateBalances = (coins) => coins.reduce((acc, { coin }) => ({
|
|
4
|
+
...acc,
|
|
5
|
+
[coin.type]: acc[coin.type] === undefined ? coin.value : acc[coin.type] + coin.value,
|
|
6
|
+
}), {});
|
|
7
|
+
export const makeDefaultCoinsAndBalancesCapability = () => {
|
|
8
|
+
const getAvailableBalances = (state) => {
|
|
9
|
+
const availableCoins = getAvailableCoins(state);
|
|
10
|
+
return calculateBalances(availableCoins);
|
|
11
|
+
};
|
|
12
|
+
const getPendingBalances = (state) => {
|
|
13
|
+
const pendingCoins = getPendingCoins(state);
|
|
14
|
+
return calculateBalances(pendingCoins);
|
|
15
|
+
};
|
|
16
|
+
const getTotalBalances = (state) => {
|
|
17
|
+
const availableBalances = getAvailableBalances(state);
|
|
18
|
+
const pendingBalances = getPendingBalances(state);
|
|
19
|
+
return pipe([availableBalances, pendingBalances], RecordOps.mergeWithAccumulator(0n, (a, b) => a + b));
|
|
20
|
+
};
|
|
21
|
+
const getAvailableCoins = (state) => {
|
|
22
|
+
const pendingSpends = new Set([...state.state.pendingSpends.values()].map(([coin]) => coin.nonce));
|
|
23
|
+
return pipe([...state.state.coins], Array.filter((coin) => !pendingSpends.has(coin.nonce)), Array.map((coin) => {
|
|
24
|
+
return {
|
|
25
|
+
coin,
|
|
26
|
+
commitment: state.coinHashes[coin.nonce].commitment,
|
|
27
|
+
nullifier: state.coinHashes[coin.nonce].nullifier,
|
|
28
|
+
};
|
|
29
|
+
}));
|
|
30
|
+
};
|
|
31
|
+
const getPendingCoins = (state) => pipe([...state.state.pendingOutputs.values()], Array.map(([coin, ttl]) => ({
|
|
32
|
+
coin,
|
|
33
|
+
ttl,
|
|
34
|
+
commitment: state.coinHashes[coin.nonce].commitment,
|
|
35
|
+
nullifier: state.coinHashes[coin.nonce].nullifier,
|
|
36
|
+
})));
|
|
37
|
+
const getTotalCoins = (state) => [...getAvailableCoins(state), ...getPendingCoins(state)];
|
|
38
|
+
return {
|
|
39
|
+
getAvailableBalances,
|
|
40
|
+
getPendingBalances,
|
|
41
|
+
getTotalBalances,
|
|
42
|
+
getAvailableCoins,
|
|
43
|
+
getPendingCoins,
|
|
44
|
+
getTotalCoins,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
2
|
+
import { ProtocolVersion, SyncProgress } from '@midnightntwrk/wallet-sdk-abstractions';
|
|
3
|
+
import { Either } from 'effect';
|
|
4
|
+
import { type WalletError } from './WalletError.js';
|
|
5
|
+
export type PublicKeys = {
|
|
6
|
+
coinPublicKey: ledger.CoinPublicKey;
|
|
7
|
+
encryptionPublicKey: ledger.EncPublicKey;
|
|
8
|
+
};
|
|
9
|
+
export declare const PublicKeys: {
|
|
10
|
+
fromSecretKeys: (secretKeys: ledger.ZswapSecretKeys) => PublicKeys;
|
|
11
|
+
};
|
|
12
|
+
export type CoinHashesMap = Readonly<Record<ledger.Nonce, {
|
|
13
|
+
nullifier: ledger.Nullifier;
|
|
14
|
+
commitment: ledger.CoinCommitment;
|
|
15
|
+
}>>;
|
|
16
|
+
export declare const CoinHashesMap: {
|
|
17
|
+
empty: {};
|
|
18
|
+
pickAllCoins(state: ledger.ZswapLocalState): readonly ledger.ShieldedCoinInfo[];
|
|
19
|
+
assertValid(map: CoinHashesMap, state: ledger.ZswapLocalState): Either.Either<void, Set<ledger.Nonce>>;
|
|
20
|
+
updateWithCoins(secretKeys: ledger.ZswapSecretKeys, existing: CoinHashesMap, coins: Iterable<ledger.ShieldedCoinInfo>): CoinHashesMap;
|
|
21
|
+
updateWithNewCoins(secretKeys: ledger.ZswapSecretKeys, existing: CoinHashesMap, coins: Iterable<ledger.ShieldedCoinInfo>): CoinHashesMap;
|
|
22
|
+
init(secretKeys: ledger.ZswapSecretKeys, coins: Iterable<ledger.ShieldedCoinInfo>): CoinHashesMap;
|
|
23
|
+
};
|
|
24
|
+
export type CoreWallet = Readonly<{
|
|
25
|
+
state: ledger.ZswapLocalState;
|
|
26
|
+
publicKeys: PublicKeys;
|
|
27
|
+
protocolVersion: ProtocolVersion.ProtocolVersion;
|
|
28
|
+
progress: SyncProgress.SyncProgress;
|
|
29
|
+
networkId: string;
|
|
30
|
+
coinHashes: CoinHashesMap;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const CoreWallet: {
|
|
33
|
+
init(localState: ledger.ZswapLocalState, secretKeys: ledger.ZswapSecretKeys, networkId: string): CoreWallet;
|
|
34
|
+
empty(publicKeys: PublicKeys, networkId: string): CoreWallet;
|
|
35
|
+
restore(localState: ledger.ZswapLocalState, secretKeys: ledger.ZswapSecretKeys, syncProgress: Omit<SyncProgress.SyncProgressData, "isConnected">, protocolVersion: bigint, networkId: string): CoreWallet;
|
|
36
|
+
restoreWithCoinHashes(publicKeys: PublicKeys, localState: ledger.ZswapLocalState, coinHashes: CoinHashesMap, syncProgress: SyncProgress.SyncProgressData, protocolVersion: bigint, networkId: string): Either.Either<CoreWallet, WalletError>;
|
|
37
|
+
initEmpty(keys: ledger.ZswapSecretKeys, networkId: string): CoreWallet;
|
|
38
|
+
applyCollapsedUpdate(wallet: CoreWallet, collapsed: ledger.MerkleTreeCollapsedUpdate): CoreWallet;
|
|
39
|
+
apply<TOffer extends ledger.ZswapOffer<ledger.Proofish>>(wallet: CoreWallet, secretKeys: ledger.ZswapSecretKeys, offer: TOffer): CoreWallet;
|
|
40
|
+
replayEventsWithChanges(wallet: CoreWallet, secretKeys: ledger.ZswapSecretKeys, events: ledger.Event[]): [CoreWallet, ledger.ZswapStateChanges[]];
|
|
41
|
+
updateProgress(wallet: CoreWallet, { appliedIndex, highestRelevantWalletIndex, highestIndex, highestRelevantIndex, isConnected, }: Partial<SyncProgress.SyncProgressData>): CoreWallet;
|
|
42
|
+
addTransaction(wallet: CoreWallet, _tx: ledger.FinalizedTransaction): CoreWallet;
|
|
43
|
+
revertTransaction<TTx extends ledger.Transaction<ledger.Signaturish, ledger.Proofish, ledger.Bindingish>>(wallet: CoreWallet, tx: TTx): CoreWallet;
|
|
44
|
+
updateTxHistory(wallet: CoreWallet, _newTxs: readonly ledger.FinalizedTransaction[]): CoreWallet;
|
|
45
|
+
spendCoins(wallet: CoreWallet, secretKeys: ledger.ZswapSecretKeys, coins: ReadonlyArray<ledger.QualifiedShieldedCoinInfo>, segment: number): [ReadonlyArray<ledger.ZswapOffer<ledger.PreProof>>, CoreWallet];
|
|
46
|
+
watchCoins(wallet: CoreWallet, secretKeys: ledger.ZswapSecretKeys, coins: ReadonlyArray<ledger.ShieldedCoinInfo>): CoreWallet;
|
|
47
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 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-v8';
|
|
14
|
+
import { ProtocolVersion, SyncProgress } from '@midnightntwrk/wallet-sdk-abstractions';
|
|
15
|
+
import { Either, Iterable, pipe, Record, Array as Arr } from 'effect';
|
|
16
|
+
import { InvalidCoinHashesError } from './WalletError.js';
|
|
17
|
+
export const PublicKeys = {
|
|
18
|
+
fromSecretKeys: (secretKeys) => {
|
|
19
|
+
return {
|
|
20
|
+
coinPublicKey: secretKeys.coinPublicKey,
|
|
21
|
+
encryptionPublicKey: secretKeys.encryptionPublicKey,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
export const CoinHashesMap = {
|
|
26
|
+
empty: {},
|
|
27
|
+
pickAllCoins(state) {
|
|
28
|
+
return [...state.coins, ...state.pendingOutputs.values().map(([coin]) => coin)];
|
|
29
|
+
},
|
|
30
|
+
assertValid(map, state) {
|
|
31
|
+
const coins = CoinHashesMap.pickAllCoins(state);
|
|
32
|
+
const coinNonces = new Set(Iterable.map(coins, (coin) => coin.nonce));
|
|
33
|
+
const definedNonces = new Set(Object.keys(map));
|
|
34
|
+
const missingNonces = coinNonces.difference(definedNonces);
|
|
35
|
+
return missingNonces.size === 0 ? Either.void : Either.left(missingNonces);
|
|
36
|
+
},
|
|
37
|
+
updateWithCoins(secretKeys, existing, coins) {
|
|
38
|
+
return Record.fromIterableWith(coins, (coin) => [
|
|
39
|
+
coin.nonce,
|
|
40
|
+
existing[coin.nonce] ?? {
|
|
41
|
+
commitment: ledger.coinCommitment(coin, secretKeys.coinPublicKey),
|
|
42
|
+
nullifier: ledger.coinNullifier(coin, secretKeys.coinSecretKey),
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
},
|
|
46
|
+
updateWithNewCoins(secretKeys, existing, coins) {
|
|
47
|
+
const newMap = CoinHashesMap.updateWithCoins(secretKeys, CoinHashesMap.empty, coins);
|
|
48
|
+
return Record.union(existing, newMap, (a) => a);
|
|
49
|
+
},
|
|
50
|
+
init(secretKeys, coins) {
|
|
51
|
+
return CoinHashesMap.updateWithCoins(secretKeys, {}, coins);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export const CoreWallet = {
|
|
55
|
+
init(localState, secretKeys, networkId) {
|
|
56
|
+
const publicKeys = PublicKeys.fromSecretKeys(secretKeys);
|
|
57
|
+
const coinHashes = CoinHashesMap.init(secretKeys, CoinHashesMap.pickAllCoins(localState));
|
|
58
|
+
const progress = SyncProgress.createSyncProgress();
|
|
59
|
+
const protocolVersion = ProtocolVersion.MinSupportedVersion;
|
|
60
|
+
return { state: localState, publicKeys, networkId, coinHashes, progress, protocolVersion };
|
|
61
|
+
},
|
|
62
|
+
empty(publicKeys, networkId) {
|
|
63
|
+
return {
|
|
64
|
+
state: new ledger.ZswapLocalState(),
|
|
65
|
+
publicKeys,
|
|
66
|
+
networkId,
|
|
67
|
+
coinHashes: CoinHashesMap.empty,
|
|
68
|
+
progress: SyncProgress.createSyncProgress(),
|
|
69
|
+
protocolVersion: ProtocolVersion.MinSupportedVersion,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
restore(localState, secretKeys, syncProgress, protocolVersion, networkId) {
|
|
73
|
+
const publicKeys = PublicKeys.fromSecretKeys(secretKeys);
|
|
74
|
+
const coinHashes = CoinHashesMap.init(secretKeys, CoinHashesMap.pickAllCoins(localState));
|
|
75
|
+
return {
|
|
76
|
+
state: localState,
|
|
77
|
+
publicKeys,
|
|
78
|
+
networkId,
|
|
79
|
+
coinHashes,
|
|
80
|
+
progress: SyncProgress.createSyncProgress(syncProgress),
|
|
81
|
+
protocolVersion: ProtocolVersion.ProtocolVersion(protocolVersion),
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
restoreWithCoinHashes(publicKeys, localState, coinHashes, syncProgress, protocolVersion, networkId) {
|
|
85
|
+
return CoinHashesMap.assertValid(coinHashes, localState).pipe(Either.mapBoth({
|
|
86
|
+
onLeft: (missingNonces) => new InvalidCoinHashesError({ message: 'Missing coin hashes for coins present in the state', missingNonces }),
|
|
87
|
+
onRight: () => ({
|
|
88
|
+
state: localState,
|
|
89
|
+
publicKeys,
|
|
90
|
+
networkId,
|
|
91
|
+
coinHashes,
|
|
92
|
+
progress: SyncProgress.createSyncProgress(syncProgress),
|
|
93
|
+
protocolVersion: ProtocolVersion.ProtocolVersion(protocolVersion),
|
|
94
|
+
}),
|
|
95
|
+
}));
|
|
96
|
+
},
|
|
97
|
+
initEmpty(keys, networkId) {
|
|
98
|
+
return this.empty(PublicKeys.fromSecretKeys(keys), networkId);
|
|
99
|
+
},
|
|
100
|
+
applyCollapsedUpdate(wallet, collapsed) {
|
|
101
|
+
const newState = wallet.state.applyCollapsedUpdate(collapsed);
|
|
102
|
+
return { ...wallet, state: newState };
|
|
103
|
+
},
|
|
104
|
+
apply(wallet, secretKeys, offer) {
|
|
105
|
+
const newState = wallet.state.apply(secretKeys, offer);
|
|
106
|
+
const newCoinHashes = CoinHashesMap.updateWithCoins(secretKeys, wallet.coinHashes, CoinHashesMap.pickAllCoins(newState));
|
|
107
|
+
return { ...wallet, state: newState, coinHashes: newCoinHashes };
|
|
108
|
+
},
|
|
109
|
+
replayEventsWithChanges(wallet, secretKeys, events) {
|
|
110
|
+
const stateWithChanges = wallet.state.replayEventsWithChanges(secretKeys, events);
|
|
111
|
+
const newState = stateWithChanges.state;
|
|
112
|
+
const newCoinHashes = CoinHashesMap.updateWithCoins(secretKeys, wallet.coinHashes, CoinHashesMap.pickAllCoins(newState));
|
|
113
|
+
const updatedWallet = { ...wallet, state: newState, coinHashes: newCoinHashes };
|
|
114
|
+
return [updatedWallet, stateWithChanges.changes];
|
|
115
|
+
},
|
|
116
|
+
updateProgress(wallet, { appliedIndex, highestRelevantWalletIndex, highestIndex, highestRelevantIndex, isConnected, }) {
|
|
117
|
+
const updatedProgress = SyncProgress.createSyncProgress({
|
|
118
|
+
appliedIndex: appliedIndex ?? wallet.progress.appliedIndex,
|
|
119
|
+
highestRelevantWalletIndex: highestRelevantWalletIndex ?? wallet.progress.highestRelevantWalletIndex,
|
|
120
|
+
highestIndex: highestIndex ?? wallet.progress.highestIndex,
|
|
121
|
+
highestRelevantIndex: highestRelevantIndex ?? wallet.progress.highestRelevantIndex,
|
|
122
|
+
isConnected: isConnected ?? wallet.progress.isConnected,
|
|
123
|
+
});
|
|
124
|
+
return { ...wallet, progress: updatedProgress };
|
|
125
|
+
},
|
|
126
|
+
// TODO: Remove after tx history is implemented
|
|
127
|
+
addTransaction(wallet, _tx) {
|
|
128
|
+
return wallet;
|
|
129
|
+
},
|
|
130
|
+
/* not implemented until this is done https://shielded.atlassian.net/browse/PM-19678 */
|
|
131
|
+
revertTransaction(wallet, tx) {
|
|
132
|
+
const newState = wallet.state.revertTransaction(tx);
|
|
133
|
+
return { ...wallet, state: newState };
|
|
134
|
+
},
|
|
135
|
+
// TODO: Remove after tx history is implemented
|
|
136
|
+
updateTxHistory(wallet, _newTxs) {
|
|
137
|
+
return wallet;
|
|
138
|
+
},
|
|
139
|
+
spendCoins(wallet, secretKeys, coins, segment) {
|
|
140
|
+
const [offers, newLocalState] = pipe(coins, Arr.reduce([[], wallet.state], ([accOffers, localState], coinToSpend) => {
|
|
141
|
+
const [nextState, newInput] = localState.spend(secretKeys, coinToSpend, segment);
|
|
142
|
+
const inputOffer = ledger.ZswapOffer.fromInput(newInput, coinToSpend.type, coinToSpend.value);
|
|
143
|
+
return [accOffers.concat([inputOffer]), nextState];
|
|
144
|
+
}));
|
|
145
|
+
const updated = { ...wallet, state: newLocalState };
|
|
146
|
+
return [offers, updated];
|
|
147
|
+
},
|
|
148
|
+
watchCoins(wallet, secretKeys, coins) {
|
|
149
|
+
const newLocalState = coins.reduce((localState, coin) => localState.watchFor(wallet.publicKeys.coinPublicKey, coin), wallet.state);
|
|
150
|
+
const newCoinHashes = CoinHashesMap.updateWithNewCoins(secretKeys, wallet.coinHashes, coins);
|
|
151
|
+
return { ...wallet, state: newLocalState, coinHashes: newCoinHashes };
|
|
152
|
+
},
|
|
153
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CoreWallet } from './CoreWallet.js';
|
|
2
|
+
import { ShieldedAddress, ShieldedCoinPublicKey, ShieldedEncryptionPublicKey } from '@midnightntwrk/wallet-sdk-address-format';
|
|
3
|
+
export type KeysCapability<TState> = {
|
|
4
|
+
getCoinPublicKey(state: TState): ShieldedCoinPublicKey;
|
|
5
|
+
getEncryptionPublicKey(state: TState): ShieldedEncryptionPublicKey;
|
|
6
|
+
getAddress(state: TState): ShieldedAddress;
|
|
7
|
+
};
|
|
8
|
+
export declare const makeDefaultKeysCapability: () => KeysCapability<CoreWallet>;
|
package/dist/v1/Keys.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ShieldedAddress, ShieldedCoinPublicKey, ShieldedEncryptionPublicKey, } from '@midnightntwrk/wallet-sdk-address-format';
|
|
2
|
+
export const makeDefaultKeysCapability = () => {
|
|
3
|
+
return {
|
|
4
|
+
getCoinPublicKey: (state) => {
|
|
5
|
+
return new ShieldedCoinPublicKey(Buffer.from(state.publicKeys.coinPublicKey, 'hex'));
|
|
6
|
+
},
|
|
7
|
+
getEncryptionPublicKey: (state) => {
|
|
8
|
+
return new ShieldedEncryptionPublicKey(Buffer.from(state.publicKeys.encryptionPublicKey, 'hex'));
|
|
9
|
+
},
|
|
10
|
+
getAddress: (state) => {
|
|
11
|
+
const coinPublicKey = new ShieldedCoinPublicKey(Buffer.from(state.publicKeys.coinPublicKey, 'hex'));
|
|
12
|
+
const encryptionPublicKey = new ShieldedEncryptionPublicKey(Buffer.from(state.publicKeys.encryptionPublicKey, 'hex'));
|
|
13
|
+
return new ShieldedAddress(coinPublicKey, encryptionPublicKey);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|